代码随想录-Day37

2024-06-24 04:20
文章标签 随想录 代码 day37

本文主要是介绍代码随想录-Day37,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
在这里插入图片描述

方法一:

/**
时间复杂度 : O(NlogN) 排序需要O(NlogN)
空间复杂度 : O(logN)  java 的内置排序是快速排序 需要 O(logN)空间*/
class Solution {public int[][] merge(int[][] intervals) {List<int[]> res = new LinkedList<>();//按照左边界排序Arrays.sort(intervals, (x, y) -> Integer.compare(x[0], y[0]));//initial start 是最小左边界int start = intervals[0][0];int rightmostRightBound = intervals[0][1];for (int i = 1; i < intervals.length; i++) {//如果左边界大于最大右边界if (intervals[i][0] > rightmostRightBound) {//加入区间 并且更新startres.add(new int[]{start, rightmostRightBound});start = intervals[i][0];rightmostRightBound = intervals[i][1];} else {//更新最大右边界rightmostRightBound = Math.max(rightmostRightBound, intervals[i][1]);}}res.add(new int[]{start, rightmostRightBound});return res.toArray(new int[res.size()][]);}
}

这段Java代码实现了一个函数 merge,用于合并一系列有重叠的区间。给定一个二维数组 intervals,其中每个 intervals[i] 是一个长度为2的数组,表示一个区间 [start, end],该函数的目标是将这些区间合并成一个新的区间数组,使得在新数组中任意两个区间没有交集,并且覆盖所有原始区间。以下是代码的详细解析:

时间复杂度和空间复杂度说明

  • 时间复杂度:O(NlogN),主要来自对区间按左边界排序的操作,其中N是区间数量。
  • 空间复杂度:O(logN),这是由于Java内置排序方法(快速排序)的递归调用栈空间。

代码解析

  1. 初始化:创建一个名为 resLinkedList 用于存储不重叠的合并后的区间。同时,初始化 start 为数组中第一个区间的起始位置,rightmostRightBound 为当前已知的最远的右边界。

  2. 排序:使用 Arrays.sort() 方法按区间的左边界升序排序整个 intervals 数组。排序是本算法的关键步骤,它确保了后续遍历过程中的合并逻辑简单且有效。

  3. 遍历并合并

    • 遍历排序后的区间数组。对于每个区间,如果当前区间的起始位置大于已知的最远右边界,说明当前区间与之前处理过的所有区间都不重叠,可以将前一个区间的结束位置与 start 组成的区间加入结果列表 res,并更新 start 为当前区间的起始位置,同时更新 rightmostRightBound 为当前区间的结束位置。
    • 如果当前区间起始位置不大于最远右边界,说明当前区间与前一个区间有重叠,此时只需要更新 rightmostRightBound 为当前区间和已知最远右边界中的较大值,以覆盖更大的范围。
  4. 添加最后一个区间:遍历结束后,别忘了将最后一个有效区间 [start, rightmostRightBound] 添加到结果列表 res 中。

  5. 转换并返回结果:最后,将 res 中的数据转换成数组类型并返回。

示例说明

假设输入区间为 [[1,3],[2,6],[8,10],[15,18]],经过排序、遍历合并后,最终的不重叠区间数组将是 [[1,6],[8,10],[15,18]],这正是函数预期输出的结果。

// 版本2
class Solution {public int[][] merge(int[][] intervals) {LinkedList<int[]> res = new LinkedList<>();Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0]));res.add(intervals[0]);for (int i = 1; i < intervals.length; i++) {if (intervals[i][0] <= res.getLast()[1]) {int start = res.getLast()[0];int end = Math.max(intervals[i][1], res.getLast()[1]);res.removeLast();res.add(new int[]{start, end});}else {res.add(intervals[i]);}         }return res.toArray(new int[res.size()][]);}
}

这个Java代码是用于解决“合并区间”问题的另一个版本实现。给定一个二维数组 intervals,其中每个 intervals[i] 为一个长度为2的数组,表示一个区间 [start, end],该函数的目标是将所有重叠的区间合并成一个新的区间数组,并且在新数组中任意两个区间不会有重叠部分。下面是该代码的详细解析:

代码解析

  1. 初始化:定义一个 LinkedList<int[]> res 作为结果容器来存储合并后的区间。使用 Arrays.sort() 方法对输入区间按起始位置升序排序,便于后续处理。

  2. 添加首个区间:将排序后的首个区间直接加入结果列表 res

  3. 遍历合并:从第二个区间开始遍历整个 intervals 数组。对于每个区间,执行以下操作:

    • 判断重叠:如果当前区间的起始位置 intervals[i][0] 小于等于结果列表中最后一个区间的结束位置 res.getLast()[1],说明这两个区间有重叠。
    • 合并区间:当发现重叠时,合并这两个区间。新的起始位置取两者中较小的(在这里直接取了 res.getLast()[0],因为当前区间起始位置肯定不会小于已加入的区间的起始位置),新的结束位置取两者结束位置的较大值。然后从 res 中移除旧的最后一个区间,添加合并后的新区间。
    • 无重叠则直接添加:如果当前区间与结果列表最后一个区间无重叠,则直接将当前区间加入到结果列表 res 中。
  4. 转换并返回结果:遍历结束后,将 LinkedList 类型的结果列表转换为数组类型并返回。

时间复杂度和空间复杂度

  • 时间复杂度:O(NlogN),其中N为区间数量。排序操作占主导地位。
  • 空间复杂度:O(N),res 用于存储不重叠的合并区间,最坏情况下需要与输入区间相同的空间。

示例说明

例如,给定输入区间 [[1,3],[2,6],[8,10],[15,18]],经过处理后,合并后的区间应为 [[1,6],[8,10],[15,18]],这正是该函数预期返回的结果。

738. 单调递增的数字

当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。

示例 1:

输入: n = 10
输出: 9
示例 2:

输入: n = 1234
输出: 1234
示例 3:

输入: n = 332
输出: 299

方法一:

版本1
class Solution {public int monotoneIncreasingDigits(int N) {String[] strings = (N + "").split("");int start = strings.length;for (int i = strings.length - 1; i > 0; i--) {if (Integer.parseInt(strings[i]) < Integer.parseInt(strings[i - 1])) {strings[i - 1] = (Integer.parseInt(strings[i - 1]) - 1) + "";start = i;}}for (int i = start; i < strings.length; i++) {strings[i] = "9";}return Integer.parseInt(String.join("",strings));}
}

这段Java代码是为了解决一个问题:给定一个非负整数N,找到具有单调递增数字的最大的数,其数字重新排列后小于或等于N。单调递增数字是指每个位置上的数字都不小于其左侧的数字(从左到右)。

代码解析

  1. 字符串转换:首先,将整数N转换成字符串数组strings,方便单独处理每一位数字。

  2. 从右向左扫描:使用一个外层循环从字符串的末尾开始向前遍历(索引istrings.length - 10),目的是检测数字是否递减。如果当前位的数字小于前一位的数字(strings[i] < strings[i - 1]),则:

    • 将前一位的数字减一((Integer.parseInt(strings[i - 1]) - 1)),并将其转换回字符串形式。
    • 记录这个位置start = i,作为后续处理的起点,因为start右侧的所有数字都需要变成9以确保结果是最大的单调递增数字。
  3. 填充9:使用一个内层循环,从位置start到字符串的末尾,将所有这些位置的数字替换为"9",以确保生成的数字尽可能大。

  4. 转换回整数并返回:最后,使用String.join("", strings)将修改后的字符串数组连接成一个字符串,再通过Integer.parseInt(...)将其转换回整数并返回。

示例

例如,如果N = 332,经过处理后的字符串数组变为["3", "2", "9"],重新组合并转换回整数得到329,这是小于等于332的最大单调递增数字。

注意:这个解法在处理边界情况(如当N本身就是单调递增或需要大幅度减少首位数字时)表现良好,但在某些情况下可能不是最高效的算法,特别是在N非常大,且大部分数字都需要减少时。

方法二:

版本2
class Solution {public int monotoneIncreasingDigits(int n) {String s = String.valueOf(n);char[] chars = s.toCharArray();int start = s.length();for (int i = s.length() - 2; i >= 0; i--) {if (chars[i] > chars[i + 1]) {chars[i]--;start = i+1;}}for (int i = start; i < s.length(); i++) {chars[i] = '9';}return Integer.parseInt(String.valueOf(chars));}
}

这个版本的代码实现与第一个版本非常相似,主要的不同在于细节处理和变量命名上,逻辑本质上是一致的。下面是这个版本的解析:

代码解析

  1. 转换为字符数组:首先,将整数n转换为字符串s,然后进一步转换成字符数组chars。这样可以直接操作每一位数字,无需频繁地在数字和字符串之间转换。

  2. 从右向左扫描:外层循环的起始点调整为i = s.length() - 2,这是因为现在是从倒数第二位开始比较(避免数组越界,同时正确定位需要检查的相邻数字对)。如果发现chars[i]大于chars[i + 1],说明违反了递增顺序:

    • chars[i]减一,表示这一位不能保持原值,需要生成更小的递增序列。
    • 更新start = i + 1,记录接下来应该全部填充为9的起始位置。
  3. 填充9:与之前版本相同,从位置start到字符串的末尾,所有这些位置的字符都被替换为字符’9’。

  4. 转换回整数并返回:最后,使用String.valueOf(chars)将字符数组转换回字符串,然后通过Integer.parseInt(...)转换为整数并返回结果。

优化点

  • 直接在字符数组上操作减少了字符串到数字、数字到字符串的转换次数,这可能在处理大数值时略微提高效率。
  • 循环起始点的调整去掉了对数组长度的减一操作,使得逻辑更直观。

示例

假设n = 332,处理过程与第一个版本相同,最终得到的字符数组表示的数字为329,转换为整数后返回。

此算法同样适用于寻找给定整数N下,最大的单调不减(即递增)整数。

这篇关于代码随想录-Day37的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089157

相关文章

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使

Java强制转化示例代码详解

《Java强制转化示例代码详解》:本文主要介绍Java编程语言中的类型转换,包括基本类型之间的强制类型转换和引用类型的强制类型转换,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录引入基本类型强制转换1.数字之间2.数字字符之间引入引用类型的强制转换总结引入在Java编程语言中,类型转换(无论

Vue 调用摄像头扫描条码功能实现代码

《Vue调用摄像头扫描条码功能实现代码》本文介绍了如何使用Vue.js和jsQR库来实现调用摄像头并扫描条码的功能,通过安装依赖、获取摄像头视频流、解析条码等步骤,实现了从开始扫描到停止扫描的完整流... 目录实现步骤:代码实现1. 安装依赖2. vue 页面代码功能说明注意事项以下是一个基于 Vue.js

mybatis-plus 实现查询表名动态修改的示例代码

《mybatis-plus实现查询表名动态修改的示例代码》通过MyBatis-Plus实现表名的动态替换,根据配置或入参选择不同的表,本文主要介绍了mybatis-plus实现查询表名动态修改的示... 目录实现数据库初始化依赖包配置读取类设置 myBATis-plus 插件测试通过 mybatis-plu