Problem
Given a list of intervals, return the minimum number of intervals you must remove so that none of the remaining intervals overlap. Two intervals that share only a single endpoint (like [1,2] and [2,3]) are not considered overlapping.
- Input:
intervals = [[1,2],[2,3],[3,4],[1,3]] - Output:
1 - Explanation: Removing [1,3] leaves [[1,2],[2,3],[3,4]], which have no overlaps.
Counter-example: [[1,2],[1,2],[1,2]] โ Output 2, because three identical intervals can only keep one.
Intuition
The problem is equivalent to keeping as many intervals as possible with no overlaps โ the answer is just total minus kept. The key insight is that if we sort by end time and always greedily accept the next interval that doesn't conflict, we leave the most room for future intervals. An interval that ends sooner can never block more future choices than one ending later.
Solution โ Greedy (Sort by End Time)
Sort intervals by end time, then scan left to right. Keep a boundary marking where the last accepted interval ends. Any interval whose start falls before that boundary overlaps with the most recently kept interval and must be removed; any interval starting at or after the boundary is safe to keep.
- Sort intervals by end time.
- Set
prev_endto the end of the first interval andintervals_to_remove = 0. - For each remaining interval (index 1 onward):
- If its start is strictly less than
prev_end, it overlaps โ incrementintervals_to_remove. - Otherwise, accept it and update
prev_endto its end.
- If its start is strictly less than
- Return
intervals_to_remove.
1def eraseOverlapIntervals(intervals: list[list[int]]) -> int:
2 intervals.sort(key=lambda interval: interval[1]) # earlier end = more room for future intervals
3
4 prev_end = intervals[0][1]
5 intervals_to_remove = 0
6
7 for i in range(1, len(intervals)):
8 if intervals[i][0] < prev_end: # starts before boundary โ overlaps, must remove
9 intervals_to_remove += 1
10 else:
11 prev_end = intervals[i][1] # no overlap: accept and advance the boundary
12
13 return intervals_to_removeTime: O(n log n) โ dominated by sorting; the scan is a single O(n) pass.
Space: O(1) โ no auxiliary data structures beyond the sorted input.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Greedy (sort by end) | O(n log n) | O(1) | All interval scheduling problems where you want to maximize kept intervals |
Common Mistakes
- Sorting by start time instead of end time โ an interval that starts earliest might extend far into the future and block many compatible intervals; sorting by end time is what guarantees we always pick the choice that leaves the most room.
- Using
<=instead of<for the overlap check โ intervals sharing only one endpoint (like [1,2] and [2,3]) are explicitly not overlapping per the problem's definition, so the condition must beintervals[i][0] < prevEnd, not<=. - Removing the current interval when you should remove the longer one โ when an overlap is detected, the greedy strategy already ensured we kept the interval with the earlier end, so the current interval (which ends later or at the same time) is always the one to discard.
- Forgetting to update
prev_endonly when keeping an interval โ if you updateprev_endunconditionally on every iteration, you lose track of the actual boundary and incorrectly allow later overlaps. - Tracking kept intervals and forgetting to subtract โ it's valid to count kept intervals and return
n - kept, but forgetting the subtraction is a silent logic error that returns a completely wrong answer.
Related Problems
merge-intervalsโ applies the same sort-by-start approach but merges overlapping intervals into one instead of counting removalsinsert-intervalโ requires merging a new interval into an already-sorted list using the same start/end overlap logicpartition-labelsโ greedy expansion of a boundary (last occurrence of each character) to form non-overlapping partitions, structurally identical to advancingprev_endgas-stationโ greedy single-pass that resets the candidate start whenever a constraint is violated, analogous to skipping an interval that breaks the non-overlap rulejump-gameโ greedy scan maintaining a running maximum reach, which plays the same role asprev_endโ a boundary that extends forward as valid choices are accepted