Skip to content

Commit 2850ab4

Browse files
committed
feat(algorithms, intervals): count min non-overlapping intervals
1 parent 747ecc1 commit 2850ab4

13 files changed

+151
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Non-Overlapping Intervals
2+
3+
Write a function to return the minimum number of intervals that must be removed from a given array intervals, where
4+
intervals[i] consists of a starting point starti and an ending point endi, to ensure that the remaining intervals do not
5+
overlap.
6+
7+
Example:
8+
9+
```text
10+
intervals = [[1,3],[5,8],[4,10],[11,13]]
11+
1
12+
13+
Explanation: Removing the interval [4,10] leaves all other intervals non-overlapping.
14+
```
15+
16+
## Solution
17+
18+
This question reduces to finding the maximum number of non-overlapping intervals. Once we know that value, then we can
19+
subtract it from the total number of intervals to get the minimum number of intervals that need to be removed.
20+
21+
![Example 1](./images/examples/count_non_overlapping_intervals_example_1.png)
22+
23+
To find the maximum number of non-overlapping intervals, we can sort the intervals by their end time. We then use a
24+
greedy approach: we iterate over each sorted interval, and repeatedly try to add that interval to the set of
25+
non-overlapping intervals. Sorting by the end time allows us to choose the intervals that end the earliest first, which
26+
frees up more time for intervals to be included later.
27+
We start by keeping track of a variable end which represents the end time of the latest interval in our set of
28+
non-overlapping intervals, as well as a variable count which represents the number of non-overlapping intervals we have
29+
found so far.
30+
31+
![Solution 1](images/solutions/count_non_overlapping_intervals_solution_1.png)
32+
![Solution 2](images/solutions/count_non_overlapping_intervals_solution_2.png)
33+
34+
We then iterate over each interval starting from the second interval in the list (the first interval is always
35+
non-overlapping). For each interval, we compare the start time of the interval to end. If it is less than end, then we
36+
cannot add the interval to our set of non-overlapping intervals, so we move onto the next interval without updating end or
37+
count.
38+
39+
![Solution 3](./images/solutions/count_non_overlapping_intervals_solution_3.png)
40+
![Solution 4](./images/solutions/count_non_overlapping_intervals_solution_4.png)
41+
42+
If it is greater than or equal to end, then we can add the interval to our set of non-overlapping intervals by updating
43+
count. We then update the value of end to be the end time of the current interval.
44+
45+
![Solution 5](./images/solutions/count_non_overlapping_intervals_solution_5.png)
46+
![Solution 6](./images/solutions/count_non_overlapping_intervals_solution_6.png)
47+
![Solution 7](./images/solutions/count_non_overlapping_intervals_solution_7.png)
48+
![Solution 8](./images/solutions/count_non_overlapping_intervals_solution_8.png)
49+
![Solution 9](./images/solutions/count_non_overlapping_intervals_solution_9.png)
50+
51+
52+
### Complexity Analysis
53+
54+
#### Time Complexity
55+
56+
O(n * logn) where n is the number of intervals. The time complexity is dominated by the sorting step.
57+
58+
#### Space Complexity
59+
60+
We only initialize two extra variables regardless of the input size.
61+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from typing import List
2+
3+
4+
def count_min_non_overlapping_intervals(intervals: List[List[int]]) -> int:
5+
"""
6+
Counts the minimum number of intervals that must be removed to make the intervals non-overlapping. Modifies the
7+
input list in place
8+
Args:
9+
intervals (List[List[int]]): The intervals to check
10+
Returns:
11+
int: number of intervals to remove
12+
"""
13+
if not intervals:
14+
return 0
15+
intervals.sort(key=lambda x: x[1])
16+
end = intervals[0][1]
17+
count = 1
18+
for i in range(1, len(intervals)):
19+
# Non-overlapping interval found
20+
if intervals[i][0] >= end:
21+
end = intervals[i][1]
22+
count += 1
23+
return len(intervals) - count
24+
25+
26+
def count_min_non_overlapping_intervals_2(intervals: List[List[int]]) -> int:
27+
"""
28+
Counts the minimum number of intervals that must be removed to make the intervals non-overlapping. Does not modify
29+
the input list in place, this instead uses a sorted copy of the input intervals
30+
Args:
31+
intervals (List[List[int]]): The intervals to check
32+
Returns:
33+
int: number of intervals to remove
34+
"""
35+
if not intervals:
36+
return 0
37+
38+
sorted_intervals = sorted(intervals, key=lambda x: x[1])
39+
40+
end = sorted_intervals[0][1]
41+
count = 1
42+
43+
for current_interval in sorted_intervals[1:]:
44+
current_interval_start, current_interval_end = current_interval
45+
# Non-overlapping interval found
46+
if current_interval_start >= end:
47+
end = current_interval_end
48+
count += 1
49+
50+
return len(sorted_intervals) - count
37.4 KB
Loading
42.2 KB
Loading
48.2 KB
Loading
41.8 KB
Loading
49.5 KB
Loading
52.5 KB
Loading
43.7 KB
Loading
51.2 KB
Loading

0 commit comments

Comments
 (0)