Efficiently Finding Overlapping Time Ranges in Python

I have a list of time ranges represented as tuples of datetime objects:
time_ranges = [
(datetime(2023, 10, 26, 10, 0, 0), datetime(2023, 10, 26, 11, 0, 0)),
(datetime(2023, 10, 26, 10, 30, 0), datetime(2023, 10, 26, 12, 0, 0)),
(datetime(2023, 10, 26, 13, 0, 0), datetime(2023, 10, 26, 14, 0, 0)),
(datetime(2023, 10, 26, 13, 30, 0), datetime(2023, 10, 26, 15, 0, 0)),
(datetime(2023, 10, 26, 15, 30, 0), datetime(2023, 10, 26, 16, 0, 0)),
]
I need to efficiently find all overlapping time ranges within this list. Two time ranges overlap if they share any common time. The output should be a list of tuples, where each tuple contains the indices of the overlapping ranges. For the example above, the desired output would be:
overlapping_ranges = [
(0, 1),
(2, 3),
]
I've tried a naive approach using nested loops to compare each range with every other range, but this has O(n^2) complexity and is too slow for my large dataset.
It would be helpful if the solution could also handle cases where a time range completely encompasses another time range (e.g., (10:00, 14:00) overlaps with (11:00, 13:00)).
Answer
I think using sweep line algorithm will the best solution.
The full code is provided below:
from datetime import datetime
from typing import List, Tuple
def find_overlapping_ranges(
time_ranges: List[Tuple[datetime, datetime]]
) -> List[Tuple[int, int]]:
events = []
for idx, (start, end) in enumerate(time_ranges):
events.append((start, 'start', idx))
events.append((end, 'end', idx))
events.sort(key=lambda x: (x[0], x[1] == 'end'))
active = set()
overlaps = []
for time, event_type, idx in events:
if event_type == 'start':
for active_idx in active:
overlaps.append((active_idx, idx))
active.add(idx)
else:
active.remove(idx)
return overlaps
time_ranges = [
(datetime(2023, 10, 26, 10, 0, 0), datetime(2023, 10, 26, 11, 0, 0)),
(datetime(2023, 10, 26, 10, 30, 0), datetime(2023, 10, 26, 12, 0, 0)),
(datetime(2023, 10, 26, 13, 0, 0), datetime(2023, 10, 26, 14, 0, 0)),
(datetime(2023, 10, 26, 13, 30, 0), datetime(2023, 10, 26, 15, 0, 0)),
(datetime(2023, 10, 26, 15, 30, 0), datetime(2023, 10, 26, 16, 0, 0)),
]
overlapping_ranges = find_overlapping_ranges(time_ranges)
print(overlapping_ranges)
Output:
[(0, 1), (2, 3)]
Enjoyed this article?
Check out more content on our blog or follow us on social media.
Browse more articles