贪心算法:区间问题
只是记录自己的刷题过程,答案参考过多种题解。
如有错误感谢指正!
参考:LeetCode 101: A LeetCode Grinding Guide (C++ Version) 作者:高畅 Chang Gao
435.无重叠区间 (Medium)
给定多个区间,计算让这些区间互不重叠所需要移除区间的最少个数。起止相连不算重叠。
A. 贪心思想
-
选择的区间 结尾越小 ,余留给其它区间的空间就越大,就越能保留更多的区间。
-
局部最优体现在,优先选右边界小的区间,所以从左向右遍历,留给下一个区间的空间就大一些。
-
全局最优体现在,选取最多的非交叉区间,即最少的移除区间。
B. 具体实现
-
按照右边界 从小到大 进行排序。
-
按照右边界升序排序,就要 从左向右遍历 。因为右边界越小越好,只要右边界越小,留给下一个区间的空间就越大,所以从左向右遍历,优先选右边界小的。(也可按照左边界升序排序,就得从右往左遍历,因为左边界数值越大越好)
-
每次选择结尾最小且和前一个选择的区间不交叉的区间。即当前组的左边界值大于等于上一组的右边界值,则把end值更新为当前组的右边界值。
-
记录非交叉区间的个数, 区间个数 =(区间总数 - 非交叉区间的个数) 。
C. 思考
-
如何判断区间不重复,采用了 判断边界值大小 的方式,涉及到判断大小最好先排序(升序或降序)。
-
每次取非交叉区间的时候,都是由右边界最小的来做 分割点 ,这样留给其他区间的范围就越大,总体的数量就能更多。
-
直接求交叉的区间是复杂的,转而求最大 非交叉 区间个数。
-
排序后思考如何遍历,不同顺序和不同遍历会出现不同效果。
-
每个小区间可以看作一个小整体,通过分配小整体的长度每个都最小,使得大整体的数量达到最多,这和分配问题相似。
D. 代码实现
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals.length < 2) {
return 0; // 零组或一组区间,直接返回零
}
// 按照边界值排序
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if (o1 != o2) {
return Integer.compare(o1[1], o2[1]);
} else {
return Integer.compare(o1[0], o2[0]);
}
}
});
int end = intervals[0][1]; // 先取第一组的右边界值
int count = 1; // 初始化非重复区间个数为1
for (int i = 1; i < intervals.length; i++) { // 从第二组开始升序遍历,寻找不重复的区间
if (end <= intervals[i][0]) { // 如果当前组的左边界值大于等于上一组的右边界值,则把end值更新为当前组的右边界值
end = intervals[i][1];
count++; // 非重复区间个数加一
}
}
return intervals.length - count; // 重复区间数 = 区间总数 - 非重复区间数
}