2025-05-27 09:57:47 +08:00
|
|
|
|
package range;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 题目: 435. 无重叠区间 (eraseOverlapIntervals)
|
|
|
|
|
|
* 描述:给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 注意 只在一点上接触的区间是 不重叠的。例如 [1, 2] 和 [2, 3] 是不重叠的。
|
|
|
|
|
|
|
|
|
|
|
|
* 示例 1:
|
|
|
|
|
|
输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
|
|
|
|
|
|
输出: 1
|
|
|
|
|
|
解释: 移除 [1,3] 后,剩下的区间没有重叠。
|
|
|
|
|
|
|
|
|
|
|
|
* 链接:https://leetcode.cn/problems/non-overlapping-intervals/
|
|
|
|
|
|
*/
|
|
|
|
|
|
//不会
|
2025-09-23 20:55:16 +08:00
|
|
|
|
//二刷不会
|
2025-10-15 20:25:53 +08:00
|
|
|
|
//三刷不会
|
2025-05-27 09:57:47 +08:00
|
|
|
|
public class EraseOverlapIntervals {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 1.按结束时间排序
|
|
|
|
|
|
* 对所有区间按它们的右端点(end)从小到大排序。
|
|
|
|
|
|
* 这样,我们每次选取能最早「结束」的区间,就能给后面留下最大的空间,容纳更多不重叠的区间。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 2.一次线性扫描,维护「当前结束」
|
|
|
|
|
|
* 用变量 end 记录上一次加入的(未移除的)区间的结束位置,初始化为排序后第一个区间的结束。
|
|
|
|
|
|
* 用 count 记录当前保留下来的不重叠区间数量,初始化为 1(第一个区间肯定留下)。
|
|
|
|
|
|
* 从第二个区间开始遍历:
|
|
|
|
|
|
* 如果当前区间的起点 start ≥ end,说明它与上一次保留的区间不重叠,可以「保留」:
|
|
|
|
|
|
* count++,并更新 end = 当前区间的 end。
|
|
|
|
|
|
* 否则,它与上一次保留的区间发生重叠,必须移除其中一个。
|
|
|
|
|
|
* 为了让后续更容易接上,我们应当移除「结束得更晚」的那个区间,也就是保留结束更早的那个(即当前排序中的那个已经在保留的区间),此时不更新 end,相当于跳过(移除)当前区间。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 3.计算需移除的数量
|
|
|
|
|
|
* 最终保留下来的区间数为 count,总区间数为 n,所以最少要移除 n - count 个。
|
|
|
|
|
|
* @param intervals
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
public int eraseOverlapIntervals(int[][] intervals) {
|
|
|
|
|
|
//0,3 2,5 4,7
|
|
|
|
|
|
int n = intervals.length;
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
|
|
// 没有区间,当然不需要移除
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 按照区间的结束时间(end)从小到大排序
|
|
|
|
|
|
Arrays.sort(intervals, (a, b) -> Integer.compare(a[1], b[1]));
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 初始化:保留第一个区间
|
|
|
|
|
|
int count = 1; // 当前保留的不重叠区间数量
|
|
|
|
|
|
int end = intervals[0][1]; // 最近一次保留区间的结束时间
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 遍历其余区间
|
|
|
|
|
|
for (int i = 1; i < n; i++) {
|
|
|
|
|
|
int startI = intervals[i][0];
|
|
|
|
|
|
int endI = intervals[i][1];
|
|
|
|
|
|
|
|
|
|
|
|
if (startI >= end) {
|
|
|
|
|
|
// 不重叠:可以保留
|
|
|
|
|
|
count++;
|
|
|
|
|
|
end = endI; // 更新「最近一次保留区间」的结束时间
|
|
|
|
|
|
}
|
|
|
|
|
|
// 否则:重叠。移除「结束更晚」的那个区间。
|
|
|
|
|
|
// 因为我们是按 end 升序遍历的,当前 intervals[i] 的 end ≥ end,
|
|
|
|
|
|
// 所以移除它即可;无需更新 end。
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 最少移除数 = 总数 - 保留下来的数量
|
|
|
|
|
|
return n - count;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|