力扣435-无重叠区间(贪心+排序 Java版详细注释题解)

力扣435-无重叠区间

一、原题题目

1.1 题目

  给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:

  1. 可以认为区间的终点总是大于它的起点。
  2. 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

1.2 示例

  • 示例1:

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

  • 示例2:

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

  • 示例3:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/non-overlapping-intervals/

二、解题思路

2.1 利用【贪心+右边界排序】解题

  • 解题思路

  看了这个题想了很久,总觉得是之前做过,但是就是没想到具体的方法,之后在题解中才回忆到这是一道区间调度问题,例如今天有很多事情安排要做,每件事情有起始时间点和结束时间点,怎么选择安排时间做事才能使处理的事情数最多。

  这里我们对每个区间的右边界进行从小到大排序,然后依次选择结束时间早并且没有区间重合的区间,有重合的就是我们需要剔除的区间。至于这样下来,为什么就是剔除的最少的?我们可以这么理解,剔除的最少也就是我们要保留的最多,为了使后面能选择的更多我们需要后面留下的空间更大,所以我们每次挑区间右边界最小的区间更合理,这里就是贪心的思路了,每次选右边界最小的就是局部最优,让后面的空间更大。这样处理完得到的结果就是全局最优。这里借用题解里一位大佬的动图演示过程。

贪心解题图解
  • 详细代码(Java)
public int eraseOverlapIntervals(int[][] intervals) {
    if(intervals.length == 0)   return 0;       // 如果数组没有元素,返回0
    Arrays.sort(intervals,(a,b)->a[1]-b[1]);		// 对数组元素按第二个值(右边界)进行升序排序
    int count = 0;                              // 统计重叠区间的个数,初始化为0
    int x_end = intervals[0][1];                // 记录现在的结束边界
    for (int i = 1;i<intervals.length;i++) {    // 从第二个元素开始遍历每一个元素
        if (intervals[i][0]<x_end){             // 当前元素的左边界小于记录的结束边界则表示重合
            count++;                            // 统计重合区间个数+1
        }
        else x_end = intervals[i][1];           // 当前元素的左边界不小于记录的右边界则表示不重合,更新记录的结束边界
    }
    return count;
}
  • 代码执行结果
哈希表映射执行结果

2.2 利用【贪心+左边界排序】解题

  • 解题思路

  在题解里还看到这样的一个思路,是对每个元素的第一个值(左边界)进行排序,碰到有重叠的区间我们选择第二个值(右边界)更小的区间保留,这里其实跟上面的思路一样,为了让后面的空间更大更能选出更多的区间,达到全局最优。代码也比较容易但性能比上一个好,这里不太明白为什么?

  • 详细代码(Java)
public int eraseOverlapIntervals(int[][] intervals) {
    if (intervals.length == 0)  return 0;           // 如果数组没有元素,返回0
    Arrays.sort(intervals,(a,b)-> a[0]-b[0]);       // 对数组元素按第一个值(左边界)进行升序排序
    int count = 0;                                  // 统计重叠区间的个数,初始化为0
    int end = intervals[0][1];                      // 记录现在的结束边界
    for (int i = 1;i<intervals.length;i++){         // 从第二个元素开始遍历每一个元素
        if (intervals[i][0]<end){                   // 当前元素左边界小于记录的结束边界则表示有重合
            count++;                                // 统计重合区间个数+1
            end = Math.min(end,intervals[i][1]);    // 更新记录的结束边界为重叠区间中右边界更小的值
        }
        else end = intervals[i][1];                 // 当前元素左边界不小于记录的右边界则表示没有重合,更新结束边界
    }
    return count;
}
  • 代码执行结果
数组映射执行结果

  在大家的思路中还看到双指针的方法,其实就是对左右边界同时排序,先左边界排序,左边界相同的时候按右边界排序,再来选择。跟上面的思路差别不大。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ItDaChuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值