Scheduling with heaps
In one line
Scheduling problems become simple when a heap always tells you the next thing that matters: the earliest-ending meeting, the shortest ready task or the most urgent job, each found in O(log n).
What it is
Many interview problems simulate time: meetings that need rooms, tasks that arrive and must run on a CPU, jobs with cooldowns or deadlines. At each moment you need one answer, such as 'which room frees up first?' or 'which ready task should run next?'. A heap keyed by that quantity answers it at the top.
The usual recipe is: sort events by time, sweep through them, and keep a heap of active or ready items. Which key you put in the heap (end time, duration, count, deadline) is the whole design decision.
Why interviewers ask
Meeting Rooms II (LeetCode 253), Single-Threaded CPU (1834), Task Scheduler (621), Reorganize String (767) and Course Schedule III (630) are common heap questions. They test whether you can turn a story into events, pick the right heap key, and handle idle time and ties. They also link to real operating system and job-queue scheduling, which interviewers like to discuss.
How it works
- 1Identify the events and sort them by time (start times, arrival times).
- 2Decide what the heap holds and its key: end times of busy rooms (min-heap), ready tasks by duration then index (min-heap), remaining counts of task types (max-heap).
- 3Sweep: before handling an event, release or add everything that has become due up to the current time (for example, pop rooms whose meeting ended, or push tasks that have arrived).
- 4Make the decision from the heap top: reuse the earliest-free room, run the shortest ready task, pick the most frequent remaining letter.
- 5If nothing is ready, jump the clock forward to the next event instead of stepping one time unit at a time.
- 6The answer is often a property of the heap (its maximum size = rooms needed) or the order of pops.
Worked example · Meeting rooms for [0, 30], [5, 10], [15, 20]
- Sort by start: [0, 30], [5, 10], [15, 20]. Heap of end times starts empty.
- [0, 30]: no room in use, open room 1. Heap = {30}.
- [5, 10]: earliest end is 30, which is after 5, so every room is busy. Open room 2. Heap = {10, 30}.
- [15, 20]: earliest end is 10 <= 15, so that room is free. Reuse it: replace 10 with 20. Heap = {20, 30}.
- End: heap size 2, so 2 rooms are needed.
- CPU version: tasks (arrival, duration) = (1, 2), (2, 4), (3, 2), (4, 1). At t = 1 only task 0 is ready, run it until t = 3. Now tasks 1 and 2 are ready; shortest is task 2 (2 units), run until t = 5. Ready: task 1 (4) and task 3 (1); run task 3 until t = 6, then task 1. Order: 0, 2, 3, 1.
Picture it
Think of an airport gate manager. They do not scan every gate when a plane lands; they keep a board sorted by 'gate free at' time and look only at the top line. If that gate is free, the plane takes it; if not, they open another gate.
Reference code
import heapq
def min_meeting_rooms(intervals):
intervals.sort() # by start time
ends = [] # min-heap: end times of rooms in use
for start, end in intervals:
if ends and ends[0] <= start: # earliest-ending room is free again
heapq.heapreplace(ends, end)
else:
heapq.heappush(ends, end) # every room busy: open a new one
return len(ends)
def cpu_order(tasks): # tasks[i] = (enqueue_time, duration)
order = sorted(range(len(tasks)), key=lambda i: tasks[i][0])
ready, result, time, i = [], [], 0, 0
while len(result) < len(tasks):
if not ready and time < tasks[order[i]][0]:
time = tasks[order[i]][0] # CPU idle: jump to the next arrival
while i < len(order) and tasks[order[i]][0] <= time:
j = order[i]
heapq.heappush(ready, (tasks[j][1], j)) # shortest job, then lowest index
i += 1
duration, j = heapq.heappop(ready)
time += duration
result.append(j)
return resultmin_meeting_rooms keeps the end times of rooms in use; if the earliest one has ended by the next start it is reused, otherwise a new room is opened, so the final heap size is the rooms needed. cpu_order sorts task indices by arrival, pushes arrived tasks keyed by (duration, index), jumps the clock when the CPU would be idle, and always runs the shortest ready task. Both run in O(n log n).
Key patterns
Minimum rooms or platforms
When: Overlapping intervals that need resources (LeetCode 253)
How: Sort by start; min-heap of end times; reuse the top if it ends by the new start, else add; answer is the heap size.
Shortest job first simulation
When: Tasks arrive over time and one CPU picks the next (LeetCode 1834)
How: Sort by arrival; push arrived tasks keyed by (duration, index); if none ready, jump time to the next arrival.
Most frequent first with cooldown
When: Task Scheduler (621) or Reorganize String (767)
How: Max-heap of remaining counts; pop the most frequent, then hold it in a waiting queue until its cooldown ends before pushing back.
Deadlines with a regret heap
When: Take as many courses or jobs as possible before deadlines (LeetCode 630)
How: Sort by deadline; add each item to a max-heap by duration; if total time exceeds the deadline, drop the longest so far.
Real-world use
Timers in runtimes
Go's runtime keeps timers in a 4-ary heap per processor, and Python's asyncio keeps scheduled callbacks in a heapq, so the next timer to fire is found in O(1) and inserted in O(log n).
Delayed job execution
Java's ScheduledThreadPoolExecutor stores tasks in a heap ordered by their trigger time; worker threads wait until the top task is due.
Operating system schedulers
Priority-based schedulers need 'highest-priority ready task next'. Real kernels use specialised structures (Linux's CFS uses a red-black tree ordered by virtual runtime), but the idea of always picking the best ready task from an ordered structure is the same.
Event-driven simulation
Discrete-event simulators for networks, queues or games keep future events in a priority queue by timestamp and repeatedly process the earliest one.
In a project
You are building the booking system for a coworking space and need to tell managers the minimum number of meeting rooms each day requires, and to assign each booking a room automatically.
- Load the day's bookings and sort them by start time.
- Keep a min-heap of (end_time, room_id) for rooms in use and a pool of free room IDs.
- For each booking, pop every room whose end time is <= the booking's start and return it to the pool; then take a free room or open a new one, and push (end, room_id).
- Record the peak heap size as the rooms needed, and report hours where the peak is reached.
Impact: Assigning 10,000 bookings takes O(n log n) and well under a second, managers see exactly how many rooms each day needs, and they can convert unused rooms after spotting days where peak demand is far below capacity.
Trade-offs
Use it when
- You repeatedly need the earliest, shortest or most urgent item while time moves forward
- Items arrive and finish dynamically, so a single sort is not enough
- The answer depends on how many items are active at once
Avoid it when
- You only need the peak overlap count: a sweep over sorted start and end times (two pointers) is simpler
- Priorities change constantly for many items: use a structure with decrease-key or an ordered set
- Timers are huge in number with coarse precision: a timing wheel (as in Kafka and Netty) gives O(1) insert
Cost: Typically O(n log n): sorting plus one push and one pop per item. Space O(n) for the heap in the worst case.
Common mistakes
- Using < instead of <= when checking whether a room is free, which treats a meeting ending at 10 and one starting at 10 as overlapping (check the problem's convention).
- Advancing the clock one unit at a time instead of jumping to the next arrival, which is too slow with large timestamps.
- Forgetting the tie-breaker (index) for tasks with equal durations, giving the wrong order.
- Popping only one finished room per booking when several may have ended; in the room-assignment version, release all of them.
- Sorting by end time when the sweep needs start times (or the reverse for interval scheduling).
Interview questions
Quick revision
- Sort events by time, sweep, keep a heap of active or ready items.
- Meeting rooms: min-heap of end times; reuse if top <= start; answer = heap size.
- CPU / SJF: heap keyed by (duration, index); jump time when idle.
- Cooldown tasks: max-heap of counts + waiting queue.
- Deadlines: max-heap by duration, drop the longest when over the deadline.
- Most solutions are O(n log n).
- Real systems: runtime timers, delayed executors, event simulators.
Check yourself
1. For meeting rooms, what does the min-heap store?
2. How many rooms do [0, 30], [5, 10], [15, 20] need?
3. Tasks (arrival, duration): (1, 2), (2, 4), (3, 2), (4, 1). In what order does a shortest-job-first CPU run them?
4. What should a scheduling simulation do when no task is ready yet?
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.