Skip to content

Commit 3683dcf

Browse files
committed
feat(algorithms): remove min intervals
1 parent 43b99df commit 3683dcf

File tree

5 files changed

+157
-2
lines changed

5 files changed

+157
-2
lines changed

algorithms/heap/min_cost_hire_k_workers/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def min_cost_to_hire_workers_2(quality, wage, k):
3030

3131
heap = [] # A max-heap (using negative values for qualities)
3232
total_quality = 0 # Sum of the qualities of the selected workers
33-
min_cost = float('inf') # Initialize the minimum cost to infinity
33+
min_cost = float("inf") # Initialize the minimum cost to infinity
3434

3535
# Step 2: Iterate through each worker sorted by their wage-to-quality ratio
3636
for ratio, q in workers:

algorithms/heap/min_cost_hire_k_workers/test_min_cost_to_hire_workers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import unittest
22
from typing import List
33
from parameterized import parameterized
4-
from algorithms.heap.min_cost_hire_k_workers import min_cost_to_hire_workers, min_cost_to_hire_workers_2
4+
from algorithms.heap.min_cost_hire_k_workers import (
5+
min_cost_to_hire_workers,
6+
min_cost_to_hire_workers_2,
7+
)
58

69
MIN_COST_TO_HIRE_K_WORKERS_TEST_CASES = [
710
([10, 20, 5], [70, 50, 30], 2, 105.00000),
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Non-overlapping Intervals
2+
3+
Given an array of intervals `intervals` where `intervals[i] = [starti, endi)` contains the half-open interval (start, endi),
4+
return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
5+
6+
> A half-open interval is one that contains only one of its boundary elements. The “(” parenthesis denotes the exclusion
7+
> of the starting point. The “]” bracket denotes the inclusion of the ending point.
8+
9+
Note that intervals which only touch at a point are non-overlapping. For example, [1, 2] and [2, 3] are non-overlapping.
10+
11+
> Note: Two intervals (a,b] and (c,d] are considered overlapping if there exists a value x such that a<x≤b and c<x≤d. In
12+
> other words, if there is any point within both intervals (excluding their starting points) where both intervals have
13+
> values, they are considered overlapping. For example, the intervals (7,11] and (10,12] are overlapping, whereas the
14+
> intervals (2,4] and (4,5] are non-overlapping.
15+
16+
## Examples
17+
18+
Example 1:
19+
```text
20+
Input: intervals = [[1,2],[2,3],[3,4],[1,3]]
21+
Output: 1
22+
Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping.
23+
```
24+
25+
Example 2:
26+
```text
27+
Input: intervals = [[1,2],[1,2],[1,2]]
28+
Output: 2
29+
Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping.
30+
```
31+
32+
Example 3:
33+
```text
34+
Input: intervals = [[1,2],[2,3]]
35+
Output: 0
36+
Explanation: You don't need to remove any of the intervals since they're already non-overlapping.
37+
```
38+
39+
## Constraints
40+
41+
- 1 <= intervals.length <= 10^5
42+
- intervals[i].length == 2
43+
- -5 * 10^4 <= starti < endi <= 5 * 10^4
44+
45+
## Topics
46+
47+
- Array
48+
- Dynamic Programming
49+
- Greedy
50+
- Sorting
51+
52+
## Solution
53+
54+
1. Sort the intervals array in an ascending order based on the end time of each interval.
55+
2. Declare two variables that will assist us in the algorithm:
56+
- end: This stores the end time of the last included interval.
57+
- remove: This stores the number of intervals to be removed. It is initialized to
58+
3. Traverse the sorted intervals array to determine which interval needs to be excluded. For each interval, the following
59+
conditions are checked:
60+
- If the start time of the current interval is greater than or equal to end, this interval does not overlap with the
61+
previously included interval and can be included. Therefore, we update end to the end time of the current interval,
62+
which is the next earliest possible end time.
63+
- Otherwise, the current interval overlaps with the previously included intervals. Therefore, it must be removed, so
64+
we increment remove.
65+
4. After the sorted intervals array has been traversed completely, there are no more intervals left to evaluate, so we
66+
return remove, which now contains the minimum number of intervals to be removed.
67+
68+
### Time Complexity
69+
70+
The time complexity of this solution is O(n log(n)), where n is the length of the `intervals` array.
71+
72+
Explanation:
73+
74+
- Time taken to sort the intervals array: O(nlog(n))
75+
- Time taken to traverse the intervals array: O(n)
76+
77+
Therefore, the overall time complexity becomes O(n + n log(n)), which simplifies to O(nlog(n)).
78+
79+
### Space Complexity
80+
81+
The space complexity of this solution is O(1).
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from typing import List
2+
import sys
3+
from math import inf
4+
5+
6+
def remove_min_intervals(intervals: List[List[int]]) -> int:
7+
# Sort the intervals based on their ending points
8+
intervals.sort(key=lambda x: x[1])
9+
10+
# Initialize a variable to keep track of the current end point
11+
end = -sys.maxsize - 1
12+
13+
# Initialize a variable to count the intervals that need to be removed
14+
remove = 0
15+
16+
# Loop through each interval in the sorted list
17+
for interval in intervals:
18+
# Check if the start point of the current interval is greater than or equal to the current end point
19+
if interval[0] >= end:
20+
# If it is, update the current end point to the end point of the current interval
21+
end = interval[1]
22+
else:
23+
# If not, increment the count of intervals to be removed
24+
remove += 1
25+
26+
# Return the count of intervals to be removed
27+
return remove
28+
29+
30+
def remove_min_intervals_2(intervals: List[List[int]]) -> int:
31+
sorted_intervals = sorted(intervals, key=lambda x: x[1])
32+
33+
current_minimum_end, count = float(-inf), 0
34+
35+
for interval_start, interval_end in sorted_intervals:
36+
if interval_start >= current_minimum_end:
37+
current_minimum_end = interval_end
38+
else:
39+
count += 1
40+
41+
return count
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import unittest
2+
from typing import List
3+
from parameterized import parameterized
4+
from algorithms.intervals.remove_min_intervals import (
5+
remove_min_intervals,
6+
remove_min_intervals_2,
7+
)
8+
9+
10+
REMOVE_MIN_INTERVALS_TEST_CASES = [
11+
([[1, 2], [2, 3], [3, 4], [1, 3]], 1),
12+
([[1, 2], [1, 2], [1, 2]], 2),
13+
([[1, 2], [2, 3]], 0),
14+
]
15+
16+
17+
class RemoveMinIntervalsTestCase(unittest.TestCase):
18+
@parameterized.expand(REMOVE_MIN_INTERVALS_TEST_CASES)
19+
def test_remove_min_intervals(self, intervals: List[List[int]], expected: int):
20+
actual = remove_min_intervals(intervals)
21+
self.assertEqual(expected, actual)
22+
23+
@parameterized.expand(REMOVE_MIN_INTERVALS_TEST_CASES)
24+
def test_remove_min_intervals_2(self, intervals: List[List[int]], expected: int):
25+
actual = remove_min_intervals_2(intervals)
26+
self.assertEqual(expected, actual)
27+
28+
29+
if __name__ == "__main__":
30+
unittest.main()

0 commit comments

Comments
 (0)