【435. 无重叠区间 中等】

发布于:2025-02-10 ⋅ 阅读:(86) ⋅ 点赞:(0)

题目:

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

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

示例 1:
输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:
输入: intervals = [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:
输入: intervals = [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。

提示:

  • 1 <= intervals.length <= 105
  • intervals[i].length == 2
  • -5 * 104 <= starti < endi <= 5 * 104

思路:

这道题有两个思路:

  1. 记录重叠区间个数,重叠区间个数就是最小移除次数
  2. 记录非交叉区间个数,最小移除次数 = 区间总数 - 非交叉区间个数

看到这道题目都冥冥之中感觉要排序,但是究竟是按照右边界排序,还是按照左边界排序呢?

其实都可以。主要就是为了让区间尽可能的重叠。

这里按照右边界排序。

针对思路2,记录非交叉区间的个数还是有技巧的,如图:
在这里插入图片描述
区间,1,2,3,4,5,6都按照右边界排好序。

当确定区间 1 和 区间2 重叠后,如何确定是否与 区间3 也重贴呢?

就是取 区间1 和 区间2 右边界的最小值,因为这个最小值之前的部分一定是 区间1 和区间2 的重合部分,如果这个最小值也触达到区间3,那么说明 区间 1,2,3都是重合的。

接下来就是找大于区间1结束位置的区间,是从区间4开始。那有同学问了为什么不从区间5开始?别忘了已经是按照右边界排序的了。

区间4结束之后,再找到区间6,所以一共记录非交叉区间的个数是三个。

总共区间个数为6,减去非交叉区间的个数3。移除区间的最小数量就是3。


代码:

  1. 思路一
class Solution {
public:
    //  按照右边界排序
    static bool cmp(vector<int>& a, vector<int>& b){
        return a[1] < b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.size() == 0) return 0;
        sort(intervals.begin(), intervals.end(), cmp);
        int result = 0;  //  记录重叠区间的个数

        for(int i = 1; i < intervals.size(); i++){
            if(intervals[i][0] < intervals[i - 1][1]){ //  如果重叠
                intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]);    //  更新区间分割点
                result++;    //  重叠区间个数加一
            }
        }
        return result;    //  重叠区间个数就是最小移除次数
    }
}; 
  1. 思路二
class Solution {
public:
    //  按照右边界排序
    static bool cmp(vector<int>& a, vector<int>& b){
        return a[1] < b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.size() == 0) return 0;
        sort(intervals.begin(), intervals.end(), cmp);
        int count = 1;  //  记录非交叉区间的个数
        int end = intervals[0][1];  //  记录区间分割点

        for(int i = 1; i < intervals.size(); i++){
            if(end <= intervals[i][0]){ //  如果不交叉
                end = intervals[i][1];  //  更新区间分割点
                count++;    //  非交叉区间个数加一
            }
        }
        return intervals.size() - count;    //  最小移除次数 = 区间个数 - 非交叉区间的个数
    }
}; 

总结:

时间复杂度:O(nlog n) ,有一个快排
空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间


参考:

代码随想录


网站公告

今日签到

点亮在社区的每一天
去签到