Back to Python Examples

Merge Intervals

Sorting first turns an O(n²) overlap-check problem into an O(n log n) single-pass greedy solution.

Contents

  1. The Problem
  2. The Sorting Insight
  3. The Solution
  4. Step-by-Step Trace
  5. Real-World Uses

1. The Problem

Given a list of intervals, merge all overlapping intervals and return the result.

# [[1,3],[2,6],[8,10],[15,18]]  →  [[1,6],[8,10],[15,18]]
#   [1,3] and [2,6] overlap (2 ≤ 3) → merged to [1,6]
#
# [[1,4],[4,5]]  →  [[1,5]]
#   Intervals that touch at a single point are considered overlapping.

2. The Sorting Insight

Without sorting, you'd have to compare every interval against every other — O(n²). Once sorted by start time, you only ever need to compare each new interval against the last merged interval. No interval before that can overlap with the current one.

# After sorting by start:  [1,3]  [2,6]  [8,10]  [15,18]
#
# [1,3] → start the merge list.   result: [[1,3]]
# [2,6] → 2 ≤ 3, overlaps!  Extend end to max(3,6)=6.  result: [[1,6]]
# [8,10]→ 8 > 6, no overlap.  Add new interval.  result: [[1,6],[8,10]]
# [15,18]→ 15 > 10, no overlap. Add new.  result: [[1,6],[8,10],[15,18]]

3. The Solution

def merge(intervals):
    intervals.sort(key=lambda x: x[0])  # Sort by start time
    merged = [intervals[0]]

    for start, end in intervals[1:]:
        last_end = merged[-1][1]
        if start <= last_end:
            # Overlapping — extend the last interval's end
            merged[-1][1] = max(last_end, end)
        else:
            # Gap — start a new interval
            merged.append([start, end])

    return merged

print(merge([[1,3],[2,6],[8,10],[15,18]]))  # [[1,6],[8,10],[15,18]]
print(merge([[1,4],[4,5]]))                   # [[1,5]]
PropertyValue
TimeO(n log n) — dominated by the sort
SpaceO(n) — output list
max(last_end, end) handles the case where one interval completely contains the next — e.g., [1,10] followed by [2,5]. Without the max, you'd incorrectly shrink the end to 5.

4. Step-by-Step Trace

Input: [[1,3],[2,6],[8,10],[15,18]] (already sorted)

CurrentLast merged endOverlap?Result
[1,3]— (first)[[1,3]]
[2,6]32 ≤ 3 ✓[[1,6]]
[8,10]68 > 6 ✗[[1,6],[8,10]]
[15,18]1015 > 10 ✗[[1,6],[8,10],[15,18]]

5. Real-World Uses

A related problem is Insert Interval — given a sorted, non-overlapping list, insert a new interval and merge. Same logic; handle the insertion point separately.