【贪心算法】力扣435:无重叠区间

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

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

思路1:贪心算法
在选择要保留区间时,区间的结尾十分重要:选择的区间结尾越小,余留给其它区间的空间就越大,就越能保留更多的区间。因此采取的贪心策略为:优先保留结尾小且不相交的区间
具体实现方法:先把区间按照结尾的大小进行增序排序,每次选择结尾最小和前一个选择的区间不重叠的区间。
注意:需要根据实际情况判断按区间开头排序还是按区间结尾排序

  • 关于为什么是按照区间右端点排序?
    这个题其实是预定会议的一个问题。给你若干时间的会议,然后去预定会议,那么能够预定的最大的会议数量是多少?核心在于我们要找到最大不重叠区间的个数。 如果我们把本题的区间看成是会议,那么按照右端点排序,我们一定能够找到一个最先结束的会议,而这个会议一定是我们需要添加到最终结果的的首个会议。(这个不难贪心得到,因为这样能够给后面预留的时间更长)。
  • 关于为什么不能按照区间左端点排序?
    同样地,我们把本题的区间看成是会议,如果“按照左端点排序,我们一定能够找到一个最先开始的会议”,但是最先开始的会议,不一定最先结束。
    举例:

image
区间a是最先开始的,如果我们采用区间a作为放入最大不重叠区间的首个区间,那么后面我们只能采用区间d作为第二个放入最大不重叠区间的区间,但这样的话,最大不重叠区间的数量为2。但是如果我们采用区间b作为放入最大不重叠区间的首个区间,那么最大不重叠区间的数量为3,因为区间b是最先结束的。

作者:Chuancey
链接:https://leetcode-cn.com/problems/non-overlapping-intervals/solution/tan-xin-jie-fa-qi-shi-jiu-shi-yi-ceng-ch-i63h/

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        n = len(intervals)
        if not intervals:
        # if n == 0:
            return 0
        intervals.sort(key=lambda x: x[1]) # 根据对象中的第二维数据的值进行升序
        right = intervals[0][1] # 从第一个区间的右端点开始进行比较
        ans = 1 # 留下来的区间数,初始化为 1
        for i in range(1, n):
            # 后面区间的左端点依次与前面的右端点比较
            if intervals[i][0] >= right:
                ans += 1
                right = intervals[i][1]
        return n - ans

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/non-overlapping-intervals/solution/wu-zhong-die-qu-jian-by-leetcode-solutio-cpsb/

时间复杂度:O(nlogn),其中 n 是区间的数量。我们需要 O(nlogn) 的时间对所有的区间按照右端点进行升序排序,并且需要 O(n) 的时间进行遍历。由于前者在渐进意义下大于后者,因此总时间复杂度为 O(nlogn)。
空间复杂度:O(logn),即为排序需要使用的栈空间。

思路2:动态规划
题目的要求等价于「选出最多数量的区间,使得它们互不重叠」。由于选出的区间互不重叠,因此我们可以将它们按照端点从小到大的顺序进行排序,并且无论我们按照左端点还是右端点进行排序,得到的结果都是唯一的。
image

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/non-overlapping-intervals/solution/wu-zhong-die-qu-jian-by-leetcode-solutio-cpsb/
由于该方法的时间复杂度较高,因此在下面的代码中,尽量使用列表推导优化常数,使得其可以在时间限制内通过所有测试数据。

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        if not intervals:
            return 0
        intervals.sort()
        n = len(intervals)
        f = [1]
        for i in range(1, n):
            f.append(max((f[j] for j in range(i) if intervals[j][1] <= intervals[i][0]), default=0) + 1)

        return n - max(f)

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/non-overlapping-intervals/solution/wu-zhong-die-qu-jian-by-leetcode-solutio-cpsb/

时间复杂度:O(n^2),其中 n 是区间的数量。我们需要 O(nlogn) 的时间对所有的区间按照左端点进行升序排序,并且需要 O(n^2) 的时间进行动态规划。由于前者在渐进意义下小于后者,因此总时间复杂度为 O(n^2)。
空间复杂度:O(n)。
注意到该方法本质上是一个最长上升子序列问题,因此我们可以将时间复杂度优化至 O(nlogn),具体可以参考「300. 最长递增子序列」。
【多人运动变种】穿上衣服我就不认识你了?来聊聊最长上升子序列

posted @   Vonos  阅读(274)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示