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 <= 105
intervals[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
interval
incacheKey
array, whilecacheResult
keeping 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
index
of current processed interval andkey
value incacheKey
array. bisect_right
here is a perform a literal binary searching on already sorted arraycacheKey
(by default, as we already sort theintervals
by end value), which get theclosest
index 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_i
overlap last[...last_end]
, consider we remove this interval,ans = ans + 1
,last_end
is un-change as we not adding this interval in to result. - If start
s_i
not overlap, consider adding this current interval into our result and update thelast_end
value
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