435-无重叠区间
一、题目
给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
二、分析
按照右边界排序,就要从左向右遍历,因为右边界越小越好,只要右边界越小,留给下一个区间的空间就越大,所以从左向右遍历,优先选右边界小的。
按照左边界排序,就要从右向左遍历,因为左边界数值越大越好(越靠右),这样就给前一个区间的空间就越大,所以可以从右向左遍历。
如果按照左边界排序,还从左向右遍历的话,要处理各个区间右边界的各种情况。
一些同学做这道题目可能真的去模拟去重复区间的行为,这是比较麻烦的,还要去删除区间。
题目只是要求移除区间的个数,没有必要去真实的模拟删除区间!
「我来按照右边界排序,从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数
了」。
此时问题就是要求非交叉区间的最大个数。
右边界排序之后,局部最优:优先选右边界小的区间
,所以从左向右遍历,留给下一个区间的空间大一些,从而尽量避免交叉。全局最优:选取最多的非交叉区间
。
局部最优推出全局最优,试试贪心!
这里记录非交叉区间的个数还是有技巧的,如图:
区间,1,2,3,4,5,6都按照右边界排好序。
每次取非交叉区间的时候,都是可右边界最小的来做分割点(这样留给下一个区间的空间就越大),所以第一条分割线就是区间1结束的位置。
接下来就是找大于区间1结束位置的区间,是从区间4开始。「那有同学问了为什么不从区间5开始?别忘已经是按照右边界排序的了」。
区间4结束之后,在找到区间6,所以一共记录非交叉区间的个数是三个。
总共区间个数为6,减去非交叉区间的个数3。移除区间的最小数量就是3。
三、实现
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
// 使用贪心思想:统计移除区间的最小数量 使剩余区间不重叠
// 要先对数组进行排序 移除的是重叠部分的区间
// 使用lambda表达式对数组进行排序 按右边界顺序排序
Arrays.sort(intervals, (a, b)->Integer.compare(a[1], b[1]));
// 遍历 统计重叠区间个数
// 记录非交叉区间个数 即不重叠区间个数 数组不为空 至少有一个不重叠区间
int count = 1;
// 记录区间结束标记 初始设置为第一个区间的结束标记
int end = intervals[0][1];
for(int i=1; i<intervals.length; i++){
// i的开始位置大于结束标记 则表示不重叠
if(end <= intervals[i][0]){
// 结束标记小于等于i的开始位置 则表示不重叠
// 更新重叠区间结束标记
end = intervals[i][1];
// 非重叠区间个数加1
count++;
}
}
// 循环结束 返回重叠区间的数量 即需要移除的
return intervals.length - count;
}
}
四、总结
-
难点一:一看题就有感觉需要排序,但究竟怎么排序,按左边界排还是右边界排。
-
难点二:排完序之后如何遍历,如果没有分析好遍历顺序,那么排序就没有意义了。
-
难点三:直接求重复的区间是复杂的,转而求最大非重复区间个数。
-
难点四:求最大非重复区间个数时,需要一个分割点来做标记。