Skip to content
ΣDSA Patterns
Menu
Language

Pattern #12

Intervals

Essential

Sort 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
[1,3]
[8,10]
[2,6]
[15,18]

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

  1. Normalize intervals (ensure start ≤ end).
  2. Sort by the key your proof needs (start for merge, end for greedy keep).
  3. Scan once, maintaining current open interval or active count.
  4. 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;
}
#StatusProblemTypeDone
  1. 1#56 Merge IntervalsGuide
  2. 2#57 Insert IntervalGuide
  3. 3#252 Meeting RoomsGuide
  4. 4#253 Meeting Rooms IIGuide
  5. 5#435 Non-overlapping IntervalsGuide
  6. 6#986 Interval List IntersectionsGuide