问题描述
435. 无重叠区间
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
示例 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
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
解题思路
换一个角度来看, 我们看移除掉重叠的区间, 剩下的元素, 后面区间的第一个元素, 一定不小于前面区间的后一个元素. 实际上还是一个求最大递增子序列 的题目, 只是由严格递增变成了非严格递增, 而比较元素大小的方式变成了比较排序后, 后面区间的左端和前面区间右端的大小. 其余保持相同.
动态规划
复制 class Solution :
def eraseOverlapIntervals ( self , intervals : List [ List [ int ]] ) -> int :
n = len (intervals)
if n == 0 :
return 0
intervals . sort ()
dp = [ 1 ] * n
max_len = 1
for j in range (n):
for i in range (j):
if intervals [ j ] [ 0 ] >= intervals [ i ] [ 1 ] and dp [ i ] >= dp [ j ]:
dp [ j ] = dp [ i ] + 1
max_len = max (max_len, dp[j])
return n - max_len
贪心
类似于[300][中等][贪心][二分][动态规划][树状数组] 最长上升子序列 中, 不断扩展最长子序列的长度. 但这里比较特殊, 不再是简单的数字之间的比较, 而是区间的比较, 但区间的大小无法定义.
我们知道递增的区间序列, 它的右端序列和左端序列肯定也是递增的. 可以对原数组的每个区间, 按区间的右端排序. 我们假设排序后, 对于位置i
的区间A
, 考虑i + 1
位置的区间B
, 如果B
与A
不重叠, 即B
的左端不小于A
的右端, 则它们可以组成递增序列; 但若两者重叠, 最长递增子序列在这一部分只能2选1, 且它与A
区间可能的关系如下:
复制 【————】 i区间, 记为A
【————】 i+1区间的一种可能, 记为B, 与区间A部分重叠
【————————————————】 i+1区间的另一种可能, 记为C, 完全涵盖A区间
对于B
和C
的情况, 能与B
或C
组成递增序列的后续区间, 也一定能与A
组成递增序列, 但反过来不行. 而且A
的右端最小, 留给后面的空间更大, 最终的递增序列一定是一系列区间右端最小的区间集合.
因此贪心算法, 我们在找下一个最长递增序列的区间时, 找到剩余的第一个与现有区间不重叠的(根据题目端点可以重叠)的区间, 这个区间一定是最终递增序列的一部分.
复制 class Solution :
def eraseOverlapIntervals ( self , intervals : List [ List [ int ]] ) -> int :
n = len (intervals)
if n == 0 :
return 0
intervals . sort (key =lambda x : x[ 1 ])
length = []
for left , right in intervals :
if len (length) == 0 or left >= length [ - 1 ]:
length . append (right)
return n - len (length)