75. Non-overlapping Intervals
Problem Statement
Given an array of 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.
Example 1:
Input: intervals = [[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.
Example 2:
Input: intervals = [[1,2],[1,2],[1,2]] Output: 2 Explanation: You need to remove two [1,2] intervals to make the rest non-overlapping.
Example 3:
Input: intervals = [[1,2],[2,3]] Output: 0 Explanation: You don't need to remove any of the intervals since they're already non-overlapping.
Solution
class Solution:
def eraseOverlapIntervals(self, intervals: list[list[int]]) -> int:
if not intervals:
return 0
# Sort intervals by their end times
# This greedy approach works because choosing the interval that ends earliest
# leaves the maximum room for subsequent non-overlapping intervals.
intervals.sort(key=lambda x: x[1])
end = intervals[0][1]
count = 1 # Start with one non-overlapping interval
for i in range(1, len(intervals)):
# If the current interval's start time is greater than or equal to
# the end time of the last chosen non-overlapping interval,
# then it does not overlap and can be included.
if intervals[i][0] >= end:
end = intervals[i][1]
count += 1
# The minimum number of intervals to remove is total intervals - max non-overlapping intervals
return len(intervals) - count
Explanation
This problem asks for the minimum number of intervals to remove to make the remaining intervals non-overlapping. This is equivalent to finding the maximum number of non-overlapping intervals and then subtracting that from the total number of intervals.
Core Idea (Greedy Approach): To maximize the number of non-overlapping intervals, we should always pick the interval that ends earliest among the available non-overlapping choices. This leaves the maximum possible space for subsequent intervals.
-
Sort by End Times: The crucial step is to sort the
intervals
based on their end times. If two intervals have the same end time, their start times can be used as a tie-breaker (though not strictly necessary for correctness in this specific problem). -
Iterate and Select Non-Overlapping:
- Initialize
end
with the end time of the first interval (after sorting). - Initialize
count
to 1, as the first interval is always part of our non-overlapping set. - Iterate through the sorted
intervals
starting from the second interval (i = 1
). - For each
current_interval
(intervals[i]
):- If
current_interval[0]
(its start time) is greater than or equal toend
(the end time of the last chosen non-overlapping interval), it means thecurrent_interval
does not overlap with the previously chosen one. We can include it in our non-overlapping set. - If included, update
end
tocurrent_interval[1]
and incrementcount
.
- If
- Initialize
-
Calculate Removals: The
count
variable now holds the maximum number of non-overlapping intervals. To find the minimum number of intervals to remove, we subtract thiscount
from the total number of intervals (len(intervals) - count
).
Time and Space Complexity:
- Time Complexity: O(N log N), primarily due to the sorting step, where N is the number of intervals. The iteration and checking part takes O(N).
- Space Complexity: O(1) if the sorting is done in-place, or O(N) if a new sorted list is created (depending on the sorting algorithm implementation).