Given an array of intervals intervals
where intervals[i] = [start_i, end_i]
, return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Note that intervals which only touch at a point are non-overlapping. For example, [1, 2]
and [2, 3]
are 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] to make the rest of the intervals 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.
Constraints:
1 <= intervals.length <= 10^5
intervals[i].length == 2
-5 * 10^4 <= start_i < end_i <= 5 * 10^4
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for non-overlapping intervals involves checking every possible combination of intervals to remove. We try removing one interval at a time, then two at a time, and so on, until we've tried all combinations.
Here's how the algorithm would work step-by-step:
def non_overlapping_intervals_brute_force(intervals):
number_of_intervals = len(intervals)
minimum_removed_intervals = number_of_intervals
for i in range(1 << number_of_intervals):
removed_intervals_count = 0
remaining_intervals = []
# Determine which intervals to keep based on the bitmask
for j in range(number_of_intervals):
if (i >> j) & 1:
remaining_intervals.append(intervals[j])
else:
removed_intervals_count += 1
# Check if the remaining intervals are non-overlapping.
is_non_overlapping = True
remaining_intervals.sort(key=lambda interval: interval[0])
for k in range(len(remaining_intervals) - 1):
if remaining_intervals[k][1] > remaining_intervals[k + 1][0]:
is_non_overlapping = False
break
# If the remaining intervals are non-overlapping, update the minimum count.
if is_non_overlapping:
# Update our best answer if we improved the number removed
minimum_removed_intervals = min(minimum_removed_intervals, removed_intervals_count)
return minimum_removed_intervals
The key to efficiently solving this problem is to focus on what intervals to keep, not which ones to remove. We want to keep as many non-overlapping intervals as possible. Sorting the intervals strategically makes the selection process straightforward.
Here's how the algorithm would work step-by-step:
def erase_overlapping_intervals(intervals):
number_of_intervals_to_remove = 0
# Sort by end times to greedily choose intervals.
intervals.sort(key=lambda interval: interval[1])
if not intervals:
return 0
# Initialize with the first interval.
end_time_of_previous_interval = intervals[0][1]
for interval_start_time, interval_end_time in intervals[1:]:
# If overlap, increment remove count
if interval_start_time < end_time_of_previous_interval:
number_of_intervals_to_remove += 1
else:
# Keep non-overlapping interval.
end_time_of_previous_interval = interval_end_time
return number_of_intervals_to_remove
Case | How to Handle |
---|---|
Empty intervals array | Return 0 as no intervals need to be removed. |
Intervals array with a single interval | Return 0 as a single interval is inherently non-overlapping. |
All intervals are identical, e.g., [[1,2],[1,2],[1,2]] | Remove all but one interval; return len(intervals) - 1. |
Intervals are already non-overlapping and sorted, e.g., [[1,2],[2,3],[3,4]] | Return 0 as no removal is needed. |
Intervals are nested, e.g., [[1,5],[2,3],[3,4]] | Greedy approach of sorting by end time correctly identifies the intervals to remove to keep smallest end times first. |
Intervals with negative start and end points, e.g., [[-2,-1],[1,2]] | Algorithm should handle negative values correctly when sorting and comparing intervals. |
Intervals with zero length, e.g., [[1,1],[2,3]] | Treat zero-length intervals as valid intervals; sorting by end time handles these correctly. |
Integer overflow when calculating interval length (though not directly applicable, consider large values) | Ensure the sorting and comparison logic don't lead to integer overflows when dealing with very large start and end values; may consider using long data types |