Problem¶
Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Constraints:
1 <= intervals.length <= 105intervals[i].length == 2-5 * 104 <= starti < endi <= 5 * 104
Example 1:
**Input:** intervals = [[1,2],[2,3],[3,4],[1,3|1,2],[2,3],[3,4],[1,3]]
**Output:** 1
**Explanation:** [1,3] can be removed and the rest of the intervals are non-overlapping.
Solve¶
Bottom-up dynamic programming¶
Dynamic problem, which is a lot similar to 2008. Maximum Earnings From Taxi, 1751. Maximum Number of Events That Can Be Attended II, we try to minimize the value of total element you want to remove.
This is bottom-up approach, where we focus on solve and building necessary all lower information first :
- Sort all intervals by end value, this to ensure when we calculate result of a interval
[s_i, e_i], we can already get the best result from range[...e_i-1]. - Try to find the best result
cacheResult[i]on each step[s_i, e_i], either:- Case 1: Remove our current interval, meaning
cacheResult[e_i] = cacheResult[e_i-1] +1 - Case 2: Use our current interval, meaning
cacheResult[e_i] = cacheResult[s_i-1] + <total_of_interval_between_s_i_and_e_i>
- Case 1: Remove our current interval, meaning
- To do this, I keeping track of already processed
intervalincacheKeyarray, whilecacheResultkeeping the best result of our current set interval ofcacheKey, adding them key by key.- The expression of above Case 1, and Case 2 replace by calculating using
indexof current processed interval andkeyvalue incacheKeyarray. bisect_righthere is a perform a literal binary searching on already sorted arraycacheKey(by default, as we already sort theintervalsby end value), which get theclosestindex wherekey[closest-1] <= value < key[closest]. Here I need to check some Off by one error to find the exact equivalence.
- The expression of above Case 1, and Case 2 replace by calculating using
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
def getEnds(interval):
return interval[1]
intervals.sort(key = getEnds)
cacheResult = []
cacheKey = []
for i, (s, e) in enumerate(intervals):
cacheKey.append(e)
if i == 0:
cacheResult[0] = 0
continue
closest = bisect_right(cacheKey, s) - 1
cacheResult.append(cacheResult[i-1] + 1)
if closest >= 0:
cacheResult[i] = min(cacheResult[i], cacheResult[closest] + i - closest -1)
return cacheResult[len(intervals) -1]
Greedy¶
There isn’t a need of caching the result, as all value with the interval with start s_i come before last end e_(i-1) is an overlap and need to be remove. These isn’t any better case at all. So we can remove them, directly increase our result by 1
- Still sort every intervals by end value
- If start
s_ioverlap last[...last_end], consider we remove this interval,ans = ans + 1,last_endis un-change as we not adding this interval in to result. - If start
s_inot overlap, consider adding this current interval into our result and update thelast_endvalue
Here is provided Answer from Leetcode
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
intervals.sort(key = lambda x: x[1])
ans = 0
k = -inf
for x, y in intervals:
if x >= k:
# Case 1
k = y
else:
# Case 2
ans += 1
return ans
Created : August 16, 2023