Merge intervals

RoadmapsDSA

Scenario

You have a list of times when your servers were down. Some downtimes overlap because multiple alerts fired simultaneously.

How do you calculate the actual contiguous blocks of time your system was offline?

Why it exists

Problem: Given an array of intervals, merge all overlapping intervals to produce a list of mutually exclusive intervals that cover all the input.

Naive approach: Compare every interval with every other interval to check for overlaps, merging them and restarting the check until no overlaps remain. This takes `O(n^2)` or worse.

Better idea: Sort the intervals first. Once sorted, overlapping intervals will be adjacent, allowing us to merge them in a single `O(n)` scan.

Mental model

If you line up all the intervals by when they start, you can just walk down the line, stretching out your current block as long as the next piece touches or overlaps it.

Sort the array by start times. Keep a 'current merged interval'. If the next interval in the sorted list overlaps the current one, extend the current one's end time. If it doesn't overlap, save the current interval and start a new one.

Repeated decision: Does this next interval (sorted by start time) overlap the one I'm currently building -- if so, extend it; if not, close it and start a new one.

Explanation

Merge Intervals has a different goal than Interval Scheduling. We aren't trying to pick a subset; we are COALESCING overlapping intervals into their union.

Because the goal is different, the sorting key is different. We MUST sort by START TIME, not end time -- the exact opposite key from Interval Scheduling, which sorts by end time because it is selecting a maximum-count subset rather than coalescing. If you sort by start time, any intervals that overlap are guaranteed to be adjacent in the sorted array.

The algorithm sweeps exactly once. We maintain the `current` interval. When we look at the `next` interval:

1. If `next.start <= current.end`, they overlap. We extend the current interval by taking the maximum of their end times: `current.end = max(current.end, next.end)`. We use `max()` because the `next` interval might be completely swallowed by the `current` one (e.g., merging `[1, 10]` with `[2, 5]`).

2. If `next.start > current.end`, they do not overlap. We push the `current` interval to our results array, and set `current = next` to start building a new contiguous block.

Let's hand-trace with intervals: `[1, 3]`, `[2, 6]`, `[8, 10]`, `[15, 18]`, `[2, 4]`.

1. Sort by start time: `[1, 3]`, `[2, 4]`, `[2, 6]`, `[8, 10]`, `[15, 18]`.

2. Initialize `current` as `[1, 3]`.

3. Next is `[2, 4]`. Start `2 <= 3` (overlap). `current.end = max(3, 4) = 4`. `current` is now `[1, 4]`.

4. Next is `[2, 6]`. Start `2 <= 4` (overlap). `current.end = max(4, 6) = 6`. `current` is now `[1, 6]`. (Notice the triple-overlap resolved perfectly).

5. Next is `[8, 10]`. Start `8 > 6` (no overlap). Push `[1, 6]` to results. `current` becomes `[8, 10]`.

6. Next is `[15, 18]`. Start `15 > 10` (no overlap). Push `[8, 10]` to results. `current` becomes `[15, 18]`.

7. End of array. Push final `current` `[15, 18]` to results.

Final output: `[1, 6]`, `[8, 10]`, `[15, 18]`.

The time complexity is `O(n log n)` due to the sorting step. The linear scan takes `O(n)`. The space complexity is `O(n)` for the output array (or `O(1)` or `O(n)` for sorting depending on the language).

Key points

Pattern: Line Sweep / Sorting

Recognition cues:

Failure signals

Engineering examples

IP/CIDR Aggregation

Firewalls often receive massive lists of blocked IP ranges that overlap.

Merging the intervals reduces the rule list to a minimal set of non-overlapping IP blocks, speeding up packet filtering.

Calendar Free/Busy Calculation

Finding a time when everyone in a team is free for a meeting.

By taking all busy events for all users and merging them, you get the combined busy time. The gaps between are the free times.

When not to use

Common mistakes

Glossary

Union
The combination of two overlapping or touching intervals into a single continuous interval.

Recall questions

Questions & answers

Given an array of intervals, return an array of non-overlapping intervals that cover all the intervals in the input.

Sort by start time. Initialize a result array with the first interval. Iterate through the rest. If the current interval overlaps the last one in the result array, update the result array's last interval end time using `max()`. Otherwise, push the current interval.

Approach: This is the classic implementation of Merge Intervals. Sorting is the critical enabler.

Given a list of intervals representing the busy times of users, find the first available free slot of a given duration.

Merge all the busy intervals across all users using the standard Merge Intervals algorithm. Then iterate through the merged intervals; the gaps between `merged[i].end` and `merged[i+1].start` are the free slots. Check if any gap is >= the requested duration.

Approach: Merging the intervals collapses the complexity of multiple users into a single timeline, making it trivial to find the gaps.

Interesting facts

Continue learning

Previous: Interval scheduling

Related: Interval scheduling

Related: Minimum Spanning Tree (Kruskal)

Return to DSA Roadmap