Back to Back
A medium Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- medium
- Seniority
- L5
- Asked in
- Uber
Problem
Given a maintenance-window export as a list of [start, end] pairs in intervals, return the merged spans as [start, end] lists ordered by start. The pairs arrive in no particular order. Ranges that overlap belong to one span, which runs from the earliest start to the latest end in its group. Two ranges also count as overlapping when one begins exactly where the other ends.
Windows stacked on windows. Find the real spans.
Example 1
Input:
intervals = [[1,6],[2,6],[8,10],[15,18]]
Output:
[[1,6],[8,10],[15,18]]
Example 2
Input:
intervals = [[1,5],[4,5]]
Output:
[[1,5]]
Worked solution and explanation
What this is really testing
Underneath the scheduling story this is interval coalescing, and the whole problem is rigged by one decision you make before writing any logic: get the intervals into start order. Do that and a single left to right sweep is enough, because every range that could merge with the current one now sits immediately to its right. Skip the ordering and you are chasing overlaps in both directions and the code turns into a mess. The part that actually separates candidates is comparing each new range against the running merged span's end, not the previous input range, so a chain like (1,4),(3,6),(5,9) collapses into (1,9) instead of leaving (5,9) stranded.
Break down the requirements
Step 1: Sort intervals by start time
Sorting the pairs puts them in start order, because Python compares [start, end] lists element by element, which brings overlapping intervals next to each other so a single left-to-right pass can merge them. Equal starts fall back to the end, which the merge does not care about.
Step 2: Merge overlapping or adjacent ranges
If the current interval's start is at or before the previous merged interval's end, extend that interval's end to the larger of the two ends. Otherwise, start a new merged interval. Treat adjacency (start == previous end) as overlap.
Step 3: Return sorted non-overlapping intervals
The output should be a list of [start, end] lists in ascending start order. Build each merged interval as a fresh list so you never mutate the caller's input.
The solution
def merge_overlapping_time_ranges(intervals):
if not intervals:
return []
# pairs compare by start first; a tie on start falls back to end, which the merge ignores
sorted_ivs = sorted(intervals)
merged = [[sorted_ivs[0][0], sorted_ivs[0][1]]]
for i in range(1, len(sorted_ivs)):
current = sorted_ivs[i]
last = merged[-1]
if current[0] <= last[1]:
if current[1] > last[1]:
last[1] = current[1]
else:
merged.append([current[0], current[1]])
return mergedCommon follow-up questions
- What if the input intervals have open and closed boundaries? (Tests tracking boundary type and adjusting the overlap condition accordingly.)
- How would you find the gaps between merged intervals? (Tests computing the complement: the space between consecutive merged intervals.)
- What if you needed to merge intervals from multiple sources? (Tests combining all intervals first, then running the merge once on the combined list.)
- How would you determine the maximum overlap count at any point? (Tests the sweep line algorithm with event sorting.)