Skip to main content

Heaps and Priority Queues

A priority queue repeatedly returns the most urgent candidate. A binary heap implements that interface by maintaining a local parent-child ordering, not a fully sorted array. Use it when new candidates arrive while you keep extracting the next smallest or largest one.

FrontendInterviews.dev Updated

Recognize the Pattern

  • Maintain the best K candidates from a stream.
  • Repeatedly choose the earliest ending interval or smallest list head.
  • Explore candidates by score rather than arrival order.

Core Invariant

Every parent outranks its children under the comparator. The root is the best candidate; siblings need not be sorted.

Important Boundary

For the K largest values, keep the smallest retained value at the root. Mutating an item's priority in place does not repair the heap.

Min heap with root 1, children 3 and 2, and children 7 and 5 below 3. Array representation is 1,3,2,7,5.
[1,3,2,7,5] is a valid min heap, although it is not sorted. Only parent-child comparisons are constrained.

The root is best, the rest need not be sorted

For a zero-indexed array, parent(i) is floor((i-1)/2), with children 2i+1 and 2i+2. A comparator returning a negative number means its first argument has higher priority.

Insertion appends a value and moves it upward until its parent is no worse. Extraction replaces the root with the last value and moves it downward toward the better child. A complete binary tree has logarithmic height, bounding both repairs.

TaskComparatorWhat the root means
Smallest number first(a, b) => a - bMinimum
Largest number first(a, b) => b - aMaximum
Earliest event first(a, b) => a.time - b.timeEarliest timestamp
Keep K largest numbersMin heap of size KWorst retained candidate

Heap, sorting, or selection?

RequirementPreferTrade-off
All values in order onceComparison sortingA typical O(N log N) baseline; a heap does not eliminate the full ordering cost.
Keep K largest values as items arriveMin heap of at most K retained itemsO(N log(K + 1)) time; retained candidates are not fully sorted.
One rank in a static mutable arrayRandomized quickselectExpected O(N), worst-case O(N squared); partitions mutate the input.
Repeated next-best choice with new candidatesPriority queuePay logarithmic repair costs instead of sorting the frontier again.

A comparator-driven binary heap

This template uses built-in JavaScript only. Empty peek and pop return undefined. Equal priorities may be returned in any order; add a secondary key if your problem requires deterministic ties.

Template Assumptions

  • A negative comparator result means the first item has higher priority; the default is a numeric min-heap.
  • peek and pop return undefined for an empty heap; priorities remain stable while queued.
JavaScript Template
class Heap {
  constructor(compare = (a, b) => a - b) {
    this.items = [];
    this.compare = compare;
  }

  get size() { return this.items.length; }
  peek() { return this.items[0]; }

  push(value) {
    const items = this.items;
    items.push(value);
    let index = items.length - 1;
    while (index > 0) {
      const parent = Math.floor((index - 1) / 2);
      if (this.compare(items[index], items[parent]) >= 0) break;
      [items[index], items[parent]] = [items[parent], items[index]];
      index = parent;
    }
  }

  pop() {
    const items = this.items;
    if (items.length === 0) return undefined;
    const first = items[0];
    const last = items.pop();
    if (items.length === 0) return first;
    items[0] = last;
    let index = 0;
    while (true) {
      const left = index * 2 + 1;
      const right = left + 1;
      let best = index;
      if (left < items.length && this.compare(items[left], items[best]) < 0) best = left;
      if (right < items.length && this.compare(items[right], items[best]) < 0) best = right;
      if (best === index) break;
      [items[index], items[best]] = [items[best], items[index]];
      index = best;
    }
    return first;
  }
}

Trace top-K by protecting the worst retained item

In Merge K Sorted Lists, store only the head of each nonempty list. After extracting one node, insert that node's successor. The frontier has at most K entries; storing every node up front discards the useful structure of the input.

  1. For K = 3, insert 3, 2, and 1 into a min heap. The root is 1.
  2. Insert 5. The heap now has four candidates, so pop its minimum, 1.
  3. Insert 6 and pop 2. Insert 4 and pop 3.
  4. The retained values are 4, 5, and 6. The root, 4, is the third largest.

Step-by-Step: Min-heap sift-up and sift-down

Step 1 / 6
1i=03i=12i=27i=35i=4
Heap array
[1,3,2,7,5]
Ordering
Valid min heap

Start with a valid min heap

Every parent is no greater than either child. The array need not be sorted.

Costs depend on frontier size

  • peek is O(1). push and pop are O(log H), where H is the current heap size. Storage is O(H).
  • The displayed constructor starts empty. Building by N repeated pushes is O(N log N) worst case; bottom-up heap construction is a separate O(N) algorithm.
  • Keeping K candidates over N values takes O(N log(K + 1)) time and O(K) space. For K=1, the work is still linear.
  • Merging N total list nodes with at most K active heads takes O(N log(K + 1)) time and O(K) heap space.
  • For a static array that needs a full sorted result once, sorting can be simpler. A heap is not automatically the best choice for every top-K question.

Do not silently change the priority

  • Re-sorting the entire candidate array after each insertion loses the heap's update bound.
  • When sinking, compare both children and swap with the better one, not always the left child.
  • Changing an object's priority after insertion can break the invariant. Reinsert a new immutable candidate or use a data structure supporting priority updates.
  • A heap does not make a graph algorithm correct by itself. Define the score, relaxation rule, and stale-entry policy separately.
  • For time-based frontend work, a heap orders events but does not implement timers, cancellation, or browser scheduling.

Mistakes and Counterexamples

Use a max heap and discard its root to keep the K largest values.

Counterexample: For K=2 and values [4,5,6], discarding the maximum removes 6 and retains [4,5], the wrong candidates.

Correction: Keep a min heap: its root is the worst retained candidate, so overflow discards 4 and keeps [5,6].

Always swap with the left child when sinking.

Counterexample: The temporary min-heap array [8,3,2] must swap 8 with 2. Swapping with 3 leaves the root larger than its right child.

Correction: Compare both existing children and choose the one with higher priority.

Mutate an item's priority without repairing the heap.

Counterexample: Two events have priorities 2 and 5. Changing the second to 1 in place leaves the old root 2 at the front.

Correction: Use supported priority updates, or push a new candidate and invalidate stale entries by identity/version. Do not mutate a retained entry in place.

Last-Minute Recap

  • State the comparator direction explicitly.
  • The root is the best candidate, not proof that the array is sorted.
  • Analyze the maximum frontier size, not only total input size.

Explain It in an Interview

  1. Why does top-K use a min-heap when looking for the largest values?
  2. When would sorting or quickselect be preferable?
  3. What must happen after changing a queued candidate's priority?

Ready to Apply This Pattern?

Before coding, explain why this pattern fits and what must stay true at each step. Start with an easier problem, then test that reasoning on a harder variation.

Ordered from Easy to Hard. Premium access requirements still apply.

References