DSAIntermediate7 min readPart 12 of 16

Intervals

Interval problems deal with ranges like meeting times, task schedules or spans of an array. The recurring trick: sort by start time, then sweep, merging or resolving overlaps as you go. It turns O(n²) pairwise checks into one pass, and it shows up in Amazon, Google, Meta and Microsoft loops.

Can you explain intervals out loud?

I'm Alex. Read this, then I'll interview you on real problems and push on the parts you skip.

When do two intervals overlap?Core idea

For inclusive intervals [start, end], two intervals overlap when each one starts before the other ends:

The overlap ruleText
overlap([a,b], [c,d]) = a <= d && c <= b

Inclusive  [1,3] and [3,5]:  DO overlap (they share 3)
Half-open  [1,3) and [3,5):  DO NOT overlap

Scheduling problems often mean half-open intervals, so ask this before writing any comparison.

Merge intervalsPattern

Sort by start, then walk through. If the next interval overlaps the one you are building, stretch its end; otherwise save it and start a new one.

Merging [1,3], [2,6], [8,10] on a number line

12345678910[1,3][2,6][8,10]merged■■■■■■■■■■■■■■■■■■■■[1,3] and [2,6] overlap on 2–3 (orange), so they merge into [1,6].[8,10] stays separate.
DiagramResult: [1,6] and [8,10].
Merge overlapping intervalsTypeScript
function merge(intervals: number[][]): number[][] {
  intervals.sort((a, b) => a[0] - b[0]);
  const res: number[][] = [];
  for (const [s, e] of intervals) {
    const last = res[res.length - 1];
    if (res.length && s <= last[1]) {
      last[1] = Math.max(last[1], e);       // overlap: extend
    } else {
      res.push([s, e]);                     // no overlap: start a new one
    }
  }
  return res;
}
TimeO(n log n)SpaceO(n)

Insert intervalPattern

The list is already sorted, so there is no need to sort again. Keep everything that ends before the new interval starts, merge everything it overlaps, then add the rest.

Insert into a sorted interval listTypeScript
function insert(intervals: number[][], newI: number[]): number[][] {
  const res: number[][] = [];
  let [ns, ne] = newI;
  let i = 0;
  while (i < intervals.length && intervals[i][1] < ns) {
    res.push(intervals[i]); i++;              // entirely before
  }
  while (i < intervals.length && intervals[i][0] <= ne) {
    ns = Math.min(ns, intervals[i][0]);
    ne = Math.max(ne, intervals[i][1]);
    i++;                                      // absorb overlaps
  }
  res.push([ns, ne]);                         // the merged interval
  while (i < intervals.length) { res.push(intervals[i]); i++; }
  return res;
}
TimeO(n)SpaceO(n)

Meeting roomsPattern

Can one person attend every meeting?

Sort by start and check that no meeting starts before the previous one ends.

Meeting Rooms ITypeScript
function canAttendAll(intervals: number[][]): boolean {
  intervals.sort((a, b) => a[0] - b[0]);
  for (let i = 1; i < intervals.length; i++) {
    if (intervals[i][0] < intervals[i - 1][1]) return false;
  }
  return true;
}

How many rooms are needed?

The answer is the largest number of meetings happening at the same time. Sort all start times and all end times separately, then sweep with two pointers: a start means one more room in use, an end means one fewer.

Meeting Rooms II: sweep over starts and endsTypeScript
function minRooms(intervals: number[][]): number {
  const starts = intervals.map(i => i[0]).sort((a, b) => a - b);
  const ends = intervals.map(i => i[1]).sort((a, b) => a - b);
  let rooms = 0, maxRooms = 0, e = 0;
  for (let s = 0; s < starts.length; s++) {
    while (e < ends.length && ends[e] <= starts[s]) { rooms--; e++; } // a room frees up
    rooms++;                                                           // this meeting takes one
    maxRooms = Math.max(maxRooms, rooms);
  }
  return maxRooms;
}
TimeO(n log n)SpaceO(n)

Non-overlapping intervals (greedy)Pattern

How many intervals must you remove so the rest never overlap? Greedy rule: always keep the interval that ends earliest, because it leaves the most room for everything after it.

Minimum removalsTypeScript
function eraseOverlap(intervals: number[][]): number {
  intervals.sort((a, b) => a[1] - b[1]);      // sort by END
  let end = -Infinity, kept = 0;
  for (const [s, e] of intervals) {
    if (s >= end) { kept++; end = e; }        // fits after the last kept one
  }
  return intervals.length - kept;             // removals
}

Interval list intersectionsPattern

Given two sorted lists of intervals, walk both with two pointers. Two intervals intersect on [max(startA, startB), min(endA, endB)]when that range is valid. Then move forward in whichever list's interval ends first.

Which sort key?Compare

ProblemSort byWhy
Merge / insert intervalsStartOverlapping intervals end up next to each other.
Meeting roomsStart (or starts and ends separately)You process meetings in the order they begin.
Non-overlapping / max activitiesEndFinishing early leaves the most room (greedy).

Common mistakesPitfalls

The wrong sort keyMerging sorts by start; greedy non-overlap sorts by end.
A wrong overlap checkFor inclusive intervals it is a <= d && c <= b, not strict less-than.
Forgetting that [1,3] and [3,5] overlapTrue for inclusive intervals; false for half-open ones.
Mutating the inputSorting in place changes the caller’s array. Copy it if the original order matters.
Edge casesEmpty input and a single interval.

Frequently asked questionsFAQ

Are interval problems always solvable in O(n log n)?

Yes for the classic set: the O(n log n) bound comes from one sort, and the sweep that follows is O(n). Insert Interval is the exception at O(n) because the input is already sorted. Problems that ask for k-schedules may also push to O(n log n) with a heap.

Do I need a heap for Meeting Rooms II?

No. Sorting starts and ends separately and then sweeping with two pointers gives the same answer in O(n log n) time with O(1) extra space. The heap version keeps each meeting's end time available for later, which reads more naturally when intervals arrive out of order in a follow-up variant.

What does "sort by end time" do differently?

Sorting by end time lets a greedy pass always pick the earliest-finishing compatible interval, which provably maximizes how many you keep. Sorting by start time breaks that invariant and returns a wrong answer on non-overlapping-interval and activity-selection variants, which is exactly why the choice of sort key matters.

Now explain intervals to me out loud

I'll give you interview problems and ask follow-ups, like a real interviewer.

Practice with Alex

Keep readingKeep reading

Ready to practice?

Reading is step one. Saying it out loud is step two.

Start a mock interview with Alex, get pushed on the parts you skip, and see your hiring signal right after.