Pattern #12
Intervals
EssentialSort by start or end, then merge, insert, or remove overlaps.
When to use
Problems give ranges [start, end] and ask merge, coverage, min removals, or room counts.
Recognition cues
- Merge intervals / insert interval
- Meeting rooms / min arrows
- Sort by start or by end
Common pitfalls
- Inclusive vs exclusive ends
- Sorting by the wrong key for the proof (end vs start)
- Not handling empty or single-interval inputs
90-second recognition drill
Which pattern fits best?
- Merge intervals / insert interval
- Meeting rooms / min arrows
- Sort by start or by end
Interactive
Mental model
A full worked walkthrough of the invariant. Pause, scrub the dots, or use ← →. Aim to narrate each step yourself.
Step 1 of 8
Unsorted intervals. Sort by start first.
How to think about it
Sort creates order. Then a linear scan either merges overlapping ranges or applies a greedy rule (e.g. always keep the interval that finishes first). Drawing on a line makes overlaps obvious.
Template shapes
| Shape | Core move | Notes |
|---|---|---|
| Merge | Sort by start | Extend end if overlap |
| Min rooms | Sweep start/end events | Track active count |
| Min removals | Sort by end | Greedy keep earliest end |
Complexity baseline
O(n log n) for the sort, then O(n) scan. Space O(n) for output or sort.
From template to problem
- Normalize intervals (ensure start ≤ end).
- Sort by the key your proof needs (start for merge, end for greedy keep).
- Scan once, maintaining current open interval or active count.
- Emit merged list or count conflicts.
Template
Same skeleton in TypeScript, Python, and C#. Adapt the invariant; keep the structure.
Intervals · Template
/** Intervals template: merge overlapping ranges after sorting by start. */
export function merge(intervals: number[][]): number[][] {
if (!intervals.length) return [];
intervals = [...intervals].sort((a, b) => a[0]! - b[0]!);
const out: number[][] = [[...intervals[0]!]];
for (let i = 1; i < intervals.length; i++) {
const cur = intervals[i]!;
const last = out[out.length - 1]!;
if (cur[0]! <= last[1]!) last[1] = Math.max(last[1]!, cur[1]!);
else out.push([...cur]);
}
return out;
}
/** Intervals template: merge overlapping ranges after sorting by start. */
export function merge(intervals: number[][]): number[][] {
if (!intervals.length) return [];
intervals = [...intervals].sort((a, b) => a[0]! - b[0]!);
const out: number[][] = [[...intervals[0]!]];
for (let i = 1; i < intervals.length; i++) {
const cur = intervals[i]!;
const last = out[out.length - 1]!;
if (cur[0]! <= last[1]!) last[1] = Math.max(last[1]!, cur[1]!);
else out.push([...cur]);
}
return out;
}
#StatusProblemTypeDifficultyDone
- 1#56 Merge IntervalsGuidemedium
- 2#57 Insert IntervalGuidemedium
- 3#252 Meeting RoomsGuideeasy
- 4#253 Meeting Rooms IIGuidemedium
- 5#435 Non-overlapping IntervalsGuidemedium
- 6#986 Interval List IntersectionsGuidemedium