-
- 合并区间
问题描述
给定一个区间的集合,合并所有重叠的区间。
代码逻辑
排序:首先按照区间的起始位置对所有区间进行升序排序。
合并:
初始化一个当前区间 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[]>() {
@Override
public 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[]>() {
@Override
public 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;
}