Leet 435. 无重叠区间

3 阅读2分钟

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠

注意 只在一点上接触的区间是 不重叠的。例如 [1, 2] 和 [2, 3] 是不重叠的。

这个问题是 LeetCode 435 题。要解决它,最核心的直觉不是去思考“要删除哪些”,而是转过头思考: “我最多能保留多少个互不重叠的区间?”

总数减去最多能保留的数量,就是我们需要移除的最小数量。


1. 通俗易懂的解题思路:贪心算法

想象你在安排一个教室的课程,为了在一天内排进尽可能多的课,你的策略应该是:哪节课结束得早,就先排哪节。

为什么?因为一节课结束得越早,留给后面课程的时间就越多。

具体步骤:

  1. 按“右边界”(结束时间)排序:把所有区间按照结束时间从小到大排个序。
  2. 选出第一节课:排好序后,第一个区间的结束时间是最早的,我们一定选它。
  3. 找下一节不冲突的课:遍历剩下的区间,如果某个区间的“开始时间”大于或等于上一个选定区间的“结束时间”,说明不重叠,把它也选进来。
  4. 计数:记录下一共选了多少个区间。

2. 关键判定条件

在遍历过程中,我们只需要比较两个值:

  • 当前区间的开始时间 (intervals[i][0])
  • 上一个选中区间的结束时间 (rightBoundary)

判定公式: > 如果 intervals[i][0] >= rightBoundary,则代表不重叠,可以保留。


3. JavaScript 代码实现

JavaScript

/**
 * @param {number[][]} intervals
 * @return {number}
 */
var eraseOverlapIntervals = function(intervals) {
    if (intervals.length === 0) return 0;

    // 1. 关键:按照每个区间的“结束时间”进行升序排序
    intervals.sort((a, b) => a[1] - b[1]);

    let count = 1; // 能够保留的区间数量,至少为 1
    let rightBoundary = intervals[0][1]; // 当前选定区间的结束时间

    for (let i = 1; i < intervals.length; i++) {
        // 2. 关键判定:如果当前区间的开始时间 >= 上一个保留区间的结束时间
        if (intervals[i][0] >= rightBoundary) {
            count++; // 找到一个不重叠的,保留它
            rightBoundary = intervals[i][1]; // 更新右边界
        }
    }

    // 3. 最终结果 = 总长度 - 最多能保留的数量
    return intervals.length - count;
};

4. 为什么不能按“左边界”排序?

这是一个常见的坑。

  • 如果按左边界排序,一个很长很长的区间如果开始得很早,它可能会跨越之后的所有区间。如果你选了它,后面所有的短区间都要被删掉,这显然不是最优解。
  • 右边界排序则保证了我们每次都给后面留下了“尽可能大”的空间。

总结

  • 排序依据:结束时间(右边界)。
  • 核心逻辑:只要不重叠,就尽量多拿。
  • 计算目标:总数 - 保留数 = 移除数。