Non-overlapping Intervals
Given an array of intervals intervals where intervals[i] = [start_i, end_i], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Note: Intervals are non-overlapping even if they have a common point. For example, [1, 3] and [2, 4] are overlapping, but [1, 2] and [2, 3] are non-overlapping.
Example 1:
Input: intervals = [[1,2],[2,4],[1,4]]
Output: 1
Explanation: After [1,4] is removed, the rest of the intervals are non-overlapping.
Example 2:
Input: intervals = [[1,2],[2,4]]
Output: 0
Constraints:
1 <= intervals.length <= 1000
intervals[i].length == 2
-50000 <= starti < endi <= 50000
Solution
A method in DP is sorting the intervals by their right endpoints. Let d p [ i ] dp[i] dp[i] denotes the maximum number of non-overlapping intervals for left i i i intervals. And one interval can get a transfer from the last non-overlapping interval in front of it. We can use binary search to find one interval’s last non-overlapping interval in front. The time complexity is O ( n log n ) O(n\log n) O(nlogn).
However, after we sort the intervals by left endpoint, where the time complexity is O ( n log n ) O(n\log n) O(nlogn), we can get the answer in O ( n ) O(n) O(n). In a greedy strategy, when we go through the intervals in ascending order, we will keep intervals with the least right endpoint, so that there will be more chances to put more intervals. So, when facing overlapping, we will keep the one will smaller right endpoint and discard the other one. Because the intervals are sorted in left endpoint ascending order, we don’t have to worry the former left endpoints and just focusing on former largest right endpoints and current endpoints.
Code
Binary search
class Solution:
def search(self, intervals, left):
le = 0
ri = len(intervals)-1
pos = -1
while le <= ri:
mid = (le+ri)//2
if intervals[mid][1] <= left:
pos = mid
le = mid+1
else:
ri = mid-1
return pos
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
intervals += [[-50001, -50001], [50001, 50001]]
intervals = sorted(intervals, key=lambda x:(x[1], x[0]))
print(intervals)
dp = [0]*len(intervals)
for i in range(1, len(dp)-1):
former = self.search(intervals, intervals[i][0])
dp[i] = max(dp[i-1], dp[former]+1)
print(dp)
return len(intervals) - dp[-2] - 2
O ( n ) O(n) O(n) after sorting
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
intervals.sort()
res = 0
prevEnd = intervals[0][1]
for start, end in intervals[1:]:
if start >= prevEnd:
prevEnd = end
else:
res += 1
prevEnd = min(end, prevEnd)
return res