Non Overlapping Removals
Problem
Given a list of intervals [start, finish], return the smallest number of them you must remove so that none of the survivors overlap. Intervals that only touch at an endpoint — one finishing exactly where the next starts — do not count as overlapping.
Examples
Input: intervals = [[1, 2], [2, 3], [3, 4], [1, 3]]
Output: 1
Why: dropping [1, 3] leaves three intervals that only touch at endpoints
Input: intervals = [[1, 2], [1, 2], [1, 2]]
Output: 2
Why: all three cover the same span, so only one can stay
Input: intervals = []
Output: 0
Why: edge case, nothing to remove
Hints
0 / 3
Minimising removals is the same as maximising how many you keep, which is usually the easier of the two to reason about.
Once the intervals are in some order, keeping an interval only constrains the ones after it through a single number.
Sort by finishing time and sweep. Keep an interval whenever it starts at or after the last kept finish, because finishing earliest leaves the most room for everything that follows. The answer is the total minus the number kept.
Solution
Sorting by finish time makes the greedy choice safe: among any set of mutually overlapping intervals, the one ending soonest blocks the least future room, so keeping it is never worse than keeping another. One sweep then keeps every interval whose start clears the last kept finish, and the removals are whatever is left over. Sorting dominates at O(n log n) time, with O(1) extra space beyond the sort.
def min_removals(intervals):
if not intervals:
return 0
intervals.sort(key=lambda p: p[1]) # earliest finisher first
kept, end = 1, intervals[0][1]
for start, finish in intervals[1:]:
if start >= end: # touching is not overlapping
kept += 1
end = finish
return len(intervals) - kept
print(min_removals([[1, 2], [2, 3], [3, 4], [1, 3]])) # -> 1
print(min_removals([[1, 2], [1, 2], [1, 2]])) # -> 2
print(min_removals([])) # -> 0Stuck on the idea rather than the code? Interval Basics & Sorting covers it.