-
- 合并区间
问题描述
给定一个区间的集合,合并所有重叠的区间。
代码逻辑
排序:首先按照区间的起始位置对所有区间进行升序排序。
合并:
初始化一个当前区间 cur 为第一个区间。
遍历所有区间,对于每个区间:
如果当前区间的起始位置小于等于 cur 的结束位置,说明它们重叠,更新 cur 的结束位置为两者结束位置的最大值。
如果不重叠,则将 cur 添加到结果列表中,并将当前区间设为新的 cur。
结果:将最终的 cur 添加到结果列表中,并将其转换为数组返回。
关键点
排序:按区间起始位置排序是合并区间的前提。
贪心思想:通过比较当前区间的起始位置和已合并区间的结束位置,决定是否合并。
时间复杂度
排序:O(nlogn)
遍历:O(n)
总时间复杂度:O(nlogn)
空间复杂度
O(n):用于存储结果的列表。
点击查看代码
//56. 合并区间public int[][] merge(int[][] intervals) {Arrays.sort(intervals,new Comparator<int[]>() {@Overridepublic int compare(int[] o1, int[] o2) {return o1[0] - o2[0];}});ArrayList<int[]> res = new ArrayList<>();int[] cur = intervals[0];for (int[] interval : intervals) {if (interval[0]<=cur[1]) {cur[1]=Math.max(interval[1],cur[1]);}else {res.add(cur);cur=interval;}}res.add(cur);return res.toArray(new int[res.size()][]);}
-
- 划分字母区间
问题描述
给定一个字符串,将字符串划分为若干个子串,使得每个子串内的字符互不相同。
代码逻辑
记录字符的最后出现位置:
使用一个数组 arr,记录每个字符在字符串中最后出现的索引。
划分区间:
遍历字符串,对于每个字符:
找到当前字符及其后续字符的最大右边界。
如果当前右边界已经是最大右边界,则将当前区间长度加入结果列表,并移动到下一个区间。
如果有更大的右边界,则更新右边界并重新验证。
优化版本:
使用一个变量 start 记录当前区间的起始位置,end 记录当前区间的最大右边界。
遍历字符串时,更新 end 为当前字符的最后出现位置。
当遍历到 end 时,说明当前区间结束,将区间长度加入结果列表,并更新 start。
关键点
字符的最后出现位置:通过数组快速查询每个字符的最后出现位置。
贪心思想:每次找到当前区间的最大右边界,确保区间内的字符互不相同。
时间复杂度
遍历字符串:O(n)
查询字符最后出现位置:O(1)
总时间复杂度:O(n)
空间复杂度
O(1):数组 arr 的大小固定为 26,不随输入规模变化。
点击查看代码
//763. 划分字母区间public List<Integer> partitionLabels(String s) {int[] arr = new int[26];for (int i = 0; i < s.length(); i++) {arr[s.charAt(i) - 'a']=i;}List<Integer> res = new ArrayList<>();for (int i = 0; i < s.length();) {int curRight = arr[s.charAt(i) - 'a'];//初始右边界while (true){int maxRight = maxRight(s,arr,i,curRight);//找最大右边界if (curRight==maxRight){//当前边界就是最大右边界res.add(curRight+1-i);i=curRight+1;break;}curRight=maxRight;//有更大的右边界就更新,重新验证该边界是不是最大右边界}}return res;/*效率一样,但这个看起来循环少,更好理解// 记录每个字符最后一次出现的位置int[] lastPositions = new int[26];char[] chars = s.toCharArray();//String拆成char数组for (int i = 0; i < chars.length; i++) {lastPositions[chars[i] - 'a'] = i;}// 初始化当前区间的起始位置和上一个区间的结束位置int start = 0, end = 0;List<Integer> result = new ArrayList<>();for (int i = 0; i < chars.length; i++) {// 更新当前字符最后一次出现的位置int endi = lastPositions[chars[i] - 'a'];if(endi < end) continue;end = endi;// end = Math.max(end, lastPositions[chars[i] - 'a']);// 如果当前字符是区间的结束字符,那么就可以分割出一个子串if (i == end) {result.add(end - start + 1);start = end + 1;}}return result;*/}private int maxRight(String s,int[] arr,int start,int end) {int max = arr[s.charAt(start) - 'a'];for (int i = start+1; i < end; i++) {max = Math.max(max,arr[s.charAt(i) - 'a']);}return max;}
-
- 无重叠区间
问题描述
给定一个区间的集合,删除最少数量的区间,使得剩下的区间互不重叠。
代码逻辑
排序:按照区间的结束位置对所有区间进行升序排序。
贪心选择:
初始化一个变量 end 为第一个区间的结束位置,计数器 count 为 1。
遍历所有区间,对于每个区间:
如果当前区间的起始位置大于等于 end,说明它们不重叠,更新 end 为当前区间的结束位置,并增加计数器。
如果重叠,则跳过当前区间。
结果:返回需要删除的区间数量,即 intervals.length - count。
关键点
排序:按区间结束位置排序是贪心选择的前提。
贪心思想:优先选择结束位置早的区间,减少后续区间的重叠。
时间复杂度
排序:O(nlogn)
遍历:O(n)
总时间复杂度:O(nlogn)
空间复杂度
O(1):仅使用了常数级别的额外空间。
点击查看代码
//435. 无重叠区间public int eraseOverlapIntervals(int[][] intervals) {Arrays.sort(intervals,new Comparator<int[]>() {@Overridepublic int compare(int[] o1, int[] o2) {if (o1[1]<o2[1]){return -1;}else if (o1[1]>o2[1]){return 1;}else {return 0;}}});int count = 1;int end = intervals[0][1];for (int i = 1; i < intervals.length; i++) {if (intervals[i][0]<end){continue;}else {end = intervals[i][1];count++;}}return intervals.length-count;}