Meeting Rooms II
Minimum rooms for all meetings — sort starts and ends, sweep line counting concurrent intervals.
intermediate3 min read
- dsa
- intervals
- interview
- Meta
- Amazon
The problem
Given meeting intervals [start, end], return the minimum number of conference rooms required.
Input: [[0,30],[5,10],[15,20]]
Output: 2
Input: [[7,10],[2,4]]
Output: 1
A room frees at end and can host a meeting starting at the same time (usually).
Brute force
For each meeting, scan how many others overlap it; take max. Correct, O(n²).
Optimal: chronological sweep
Separate starts and ends. Sort both. Walk with two pointers:
- If next start < next end → need a new room (
used++) - Else a meeting ended → free a room (
used--) - Track
max(used)
function minMeetingRooms(intervals: number[][]): number {
if (!intervals.length) return 0;
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 s = 0;
let e = 0;
let used = 0;
let best = 0;
while (s < starts.length) {
if (starts[s] < ends[e]) {
used++;
best = Math.max(best, used);
s++;
} else {
used--;
e++;
}
}
return best;
}
| Time | O(n log n) |
| Space | O(n) |
Heap alternative
Sort meetings by start. Min-heap of end times of rooms in use. For each meeting: if earliest end ≤ start, pop (reuse). Push this end. Heap size = rooms used; track max.
// Interview sketch with sorted array as fake min-heap of ends
function minMeetingRoomsHeap(intervals: number[][]): number {
intervals.sort((a, b) => a[0] - b[0]);
const ends: number[] = []; // keep sorted ascending
for (const [start, end] of intervals) {
if (ends.length && ends[0] <= start) {
ends.shift(); // free earliest room
}
// insert end in order
let i = 0;
while (i < ends.length && ends[i] < end) i++;
ends.splice(i, 0, end);
}
return ends.length; // wrong: need max size during process
}
Correct heap version tracks max size as you go:
function minMeetingRoomsHeapCorrect(intervals: number[][]): number {
intervals.sort((a, b) => a[0] - b[0]);
const ends: number[] = [];
let best = 0;
for (const [start, end] of intervals) {
if (ends.length && ends[0] <= start) ends.shift();
let i = 0;
while (i < ends.length && ends[i] < end) i++;
ends.splice(i, 0, end);
best = Math.max(best, ends.length);
}
return best;
}
Prefer the two-pointer sweep in interviews — less fiddly than coding a heap in JS.
Edge cases
- Empty → 0
- All sequential non-overlapping → 1
- All overlapping one point in time → n
start == endzero-duration meetings- Many ending exactly when others start → reuse (
starts[s] < ends[e]strict)
Common bugs
- Sorting intervals only once and counting adjacent (that’s Meeting Rooms I)
- Using
<=wrong so rooms aren’t reused at exact boundaries - Returning final
usedinstead of peak
Interview delivery
- Peak concurrency problem.
- Sort starts & ends; two pointers.
- Or min-heap of ends.
- O(n log n).
- Trace
[[0,30],[5,10],[15,20]]→ peak 2.