Skip to content

Commit e7de547

Browse files
authored
Merge pull request #119 from BrianLusina/feat/interval-list-intersections
feat(algorithms, intervals): interval list intersections
2 parents ce15ab0 + 0f39f65 commit e7de547

18 files changed

+242
-1
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@
9595
* Intervals
9696
* Insert Interval
9797
* [Test Insert Interval](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/intervals/insert_interval/test_insert_interval.py)
98+
* Interval Intersection
99+
* [Test Intervals Intersection](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/intervals/interval_intersection/test_intervals_intersection.py)
98100
* Meeting Rooms
99101
* [Test Min Meeting Rooms](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/intervals/meeting_rooms/test_min_meeting_rooms.py)
100102
* Merge Intervals
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Interval List Intersections
2+
3+
Given two lists of closed intervals, interval_list_a and interval_list_b, return the intersection of the two interval
4+
lists.
5+
6+
> A closed interval [start, end] (with start <= end) includes all real numbers x such that start <= x <= end.
7+
8+
Each interval in the lists has its own start and end time and is represented as [start, end]. Specifically:
9+
10+
- interval_list_a[i] = [starti, endi]
11+
- interval_list_b[j] = [startj, endj]
12+
13+
The intersection of two closed intervals i and j is either:
14+
- An empty set, if they do not overlap, or
15+
- A closed interval [max(starti, startj), min(endi, endj)] if they do overlap.
16+
17+
Also, each list of intervals is pairwise disjoint(intervals within a list don't overlap) and in sorted order.
18+
19+
## Constraints
20+
21+
- 0 <= `interval_list_a.length`, `interval_list_b.length` <= 1000
22+
- `interval_list_a.length` + `interval_list_b.length` >= 1
23+
- 0 <= `starti` < `endi` <= 10^9
24+
- `endi` < `start(i+1)`
25+
- 0 <= `startj` < `endj` <= 10^9
26+
- `endj` < `start(j+1)`
27+
28+
## Examples
29+
30+
![Example 1](./images/examples/interval_intersection_example_1.png)
31+
![Example 2](./images/examples/interval_intersection_example_2.png)
32+
![Example 3](./images/examples/interval_intersection_example_3.png)
33+
![Example 4](./images/examples/interval_intersection_example_4.png)
34+
![Example 5](./images/examples/interval_intersection_example_5.png)
35+
36+
## Topics
37+
38+
- Two pointers
39+
- Intervals
40+
41+
## Solutions
42+
43+
1. [Naive Approach](#naive-approach)
44+
1. [Using Intervals](#optimized-approach-using-intervals)
45+
46+
### Naive Approach
47+
48+
The naive approach for this problem is to use a nested loop for finding intersecting intervals.
49+
50+
The outer loop will iterate for every interval in interval_list_a and the inner loop will search for any intersecting
51+
interval in the interval_list_b.
52+
53+
If such an interval exists, we add it to the intersections list.
54+
55+
Since we are using nested loops, the time complexity for this naive approach will be O(n*m), where n is the length of
56+
intervalsA and m is the length of intervalsB.
57+
58+
### Optimized approach using intervals
59+
60+
The essence of this approach is to leverage two key advantages: first, the lists of intervals are sorted, and second,
61+
the result requires comparing intervals to check for overlap. The algorithm works by iterating through both sorted lists
62+
of intervals simultaneously, identifying intersections between intervals from the two lists. At each step, it compares
63+
the current intervals from both lists and determines whether there is an intersection by examining the endpoints of the
64+
intervals. It adds the intersecting interval to the result list if an intersection exists. To efficiently navigate
65+
through the intervals, the algorithm adjusts pointers based on the positions of the intervals’ endpoints, ensuring that
66+
it covers all possible intersections. The algorithm accurately computes the interval list intersection by systematically
67+
traversing the lists, identifying intersections, and adding them to the results list, the algorithm accurately computes
68+
the interval list intersection.
69+
70+
The algorithm to solve this problem is as follows:
71+
72+
- We’ll use two indexes, i and j, to iterate through the intervals in both lists, `interval_list_a` and `interval_list_b`,
73+
respectively.
74+
- To check whether there’s any intersecting point among the given intervals:
75+
- Take the starting times of the first pair of intervals from both lists and check which occurs later, storing it in
76+
a variable, say start.
77+
- Also, compare the ending times of the same pair of intervals from both lists and store the minimum end time in
78+
another variable, say, end.
79+
80+
![Solution 1](./images/solutions/interval_intersection_solution_1.png)
81+
![Solution 2](./images/solutions/interval_intersection_solution_2.png)
82+
83+
- Next, we will check if `interval_list_a[i]` and `interval_list_b[j]` overlap by comparing the start and end times.
84+
- If the times overlap, then the intersecting time interval will be added to the resultant list, that is, intersections.
85+
- After the comparison, we need to move forward in one of the two input lists. The decision is taken based on which
86+
of the two intervals being compared ends earlier. If the interval that ends first is in `interval_list_a`, we move
87+
forward in that list, else, we move forward in `interval_list_b`.
88+
89+
The illustrations below show the key steps of the solution.
90+
91+
![Solution 3](./images/solutions/interval_intersection_solution_3.png)
92+
![Solution 4](./images/solutions/interval_intersection_solution_4.png)
93+
![Solution 5](./images/solutions/interval_intersection_solution_5.png)
94+
![Solution 6](./images/solutions/interval_intersection_solution_6.png)
95+
![Solution 7](./images/solutions/interval_intersection_solution_7.png)
96+
![Solution 8](./images/solutions/interval_intersection_solution_8.png)
97+
98+
#### Solution summary
99+
100+
Let’s briefly discuss the approach that we have used to solve the above mentioned problem:
101+
102+
- Set two pointers, i and j, at the beginning of both lists, respectively, for their iteration.
103+
- While iterating, find the latest starting time and the earliest ending time for each pair of intervals
104+
`interval_list_a[i]` and `interval_list_b[j]`.
105+
- If the latest starting time is less than or equal to the earliest ending time, store it as an intersection.
106+
- Increment the pointer (i or j) of the list having the smaller end time of the current interval.
107+
- Keep iterating until either list is fully traversed.
108+
- Return the list of intersections.
109+
110+
#### Time Complexity
111+
112+
The time complexity is `O(n+m)`, where n and m are the number of meetings in `interval_list_a` and `interval_list_b`,
113+
respectively.
114+
115+
#### Space Complexity
116+
117+
The space complexity is `O(1)` as only a fixed amount of memory is consumed by a few temporary variables for computations
118+
performed by the algorithm.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from typing import List
2+
3+
4+
def intervals_intersection(
5+
interval_list_a: List[List[int]], interval_list_b: List[List[int]]
6+
) -> List[List[int]]:
7+
"""
8+
This function finds the intersections between interval_list_a and interval_list_b and returns the list of
9+
intersections.
10+
11+
Time Complexity is O(n+m): where n is the length of list a and m is the length of list b
12+
Space Complexity is O(1) for auxiliary space as no extra space is required other than the pointers used to move along
13+
both lists. However, the resultant list returned can have a worst case of O(n+m) in the case we have either list
14+
having multiple intervals that are all intersections in the other list.
15+
16+
Args:
17+
interval_list_a(list): list 'a' of intervals.
18+
interval_list_b(list): list 'b' of intervals.
19+
Returns:
20+
list: list of intersected intervals
21+
"""
22+
if not interval_list_a and not interval_list_b:
23+
return []
24+
# pointers that will move along the interval lists, i moves along the intervals on interval_list_a, while j moves
25+
# along the intervals on interval_list_b
26+
i, j = 0, 0
27+
28+
# the final result list
29+
intersected_intervals = []
30+
31+
interval_list_a_len = len(interval_list_a)
32+
interval_list_b_len = len(interval_list_b)
33+
34+
# while loop will break whenever either of the lists ends
35+
while i < interval_list_a_len and j < interval_list_b_len:
36+
# Let's check if interval_list_a[i] intersects interval_list_b[j]
37+
interval_a = interval_list_a[i]
38+
interval_b = interval_list_b[j]
39+
40+
# extract the start and end times of each interval
41+
start_a, end_a = interval_a
42+
start_b, end_b = interval_b
43+
44+
# Get the potential startpoint and endpoint of the closed interval intersection
45+
closed_interval_start = max(start_a, start_b)
46+
closed_interval_end = min(end_a, end_b)
47+
48+
# if this is an actual intersection
49+
if closed_interval_start <= closed_interval_end:
50+
# add it to the list
51+
closed_interval = [closed_interval_start, closed_interval_end]
52+
intersected_intervals.append(closed_interval)
53+
54+
# Move forward in the list whose interval ends earlier
55+
if end_a <= end_b:
56+
i += 1
57+
else:
58+
j += 1
59+
60+
return intersected_intervals
102 KB
Loading
111 KB
Loading
108 KB
Loading
102 KB
Loading
78.1 KB
Loading
90.3 KB
Loading
85.5 KB
Loading

0 commit comments

Comments
 (0)