InterviewKata
Data structure 8 of 12

Heaps and priority queues

A heap always knows its smallest item, and hands it over in O(log n). It's the structure behind every priority queue: "serve the most urgent next", whatever order things arrived in.

A 3-minute conversation: Sam asks, Priya explains
0:00 / 2:40

1. What it is

A hospital emergency room. Patients don't go in arrival order: the most urgent case is seen next. When a new patient arrives, the nurse doesn't re-sort the whole waiting room, she just works out roughly where the newcomer fits. That rough, cheap ordering is a heap.

A min-heap keeps the smallest item at the top, always. A max-heap keeps the largest. It is not fully sorted: it only guarantees that every parent is smaller than (or equal to) its children. That weaker promise is what makes it fast.

2. How it sits in memory

A heap is a binary tree packed into a plain list, level by level. The children of index i sit at 2i + 1 and 2i + 2; its parent is at (i − 1) // 2. No pointers needed.

Push: add at the end, then sift up: swap with the parent while smaller. Pop: take the top, move the last item to the top, then sift down: swap with the smaller child while bigger. Each walks one path of a tree of height log n.

3. Operations and what they cost

OperationPython heapqCost
Peek the smallesth[0]O(1)
Pushheapq.heappush(h, x)O(log n)
Pop the smallestheapq.heappop(h)O(log n)
Build from a listheapq.heapify(nums)O(n)
Find or remove an arbitrary item—O(n): heaps only know the top
Heap or sort?Sorting everything is O(n log n) once. A heap is better when items keep arriving, or when you only need the top few: k pops cost O(k log n).

4. In Python, TypeScript and Java

Python's heapq works on a plain list and is a min-heap. For a max-heap, push negated numbers. Java's PriorityQueue takes a comparator. JavaScript has no built-in heap; interview solutions write a small one (operation A below) or use a sorted array for tiny inputs.

import heapq
    h = []
    heapq.heappush(h, 5)
    heapq.heappush(h, 1)
    heapq.heappush(h, 3)
    smallest = h[0]              # 1, peek
    first = heapq.heappop(h)     # 1
    nums = [9, 4, 7]
    heapq.heapify(nums)          # turns a list into a heap, O(n)

    max_h = []                   # max-heap: store negatives
    heapq.heappush(max_h, -5)
    biggest = -max_h[0]          # 5

    tasks = []                   # priority queue of (priority, item)
    heapq.heappush(tasks, (2, "email"))
    heapq.heappush(tasks, (1, "fix bug"))
    heapq.heappop(tasks)         # (1, 'fix bug')

5. Core operations, step by step

A heap built by hand, so sift up and sift down stop being mysterious, then the two ways you'll use heapq.

A · A min-heap by hand: push and sift up

What it doesAppends the new item, then swaps it with its parent until the parent is smaller.
Picture itA new patient sits at the end of the waiting room, then keeps swapping with the person ahead while more urgent.
CostO(log n): at most one swap per level.

The code. Parent of index i: (i − 1) // 2.

def heap_push(h, x):
    h.append(x)
    i = len(h) - 1
    while i > 0:
        parent = (i - 1) // 2
        if h[parent] <= h[i]:
            break
        h[parent], h[i] = h[i], h[parent]
        i = parent
    return h

Line by line.

LineWhat it doesWhy
h.append(x)The new item goes in the next free spot.Keeps the tree complete: no gaps in the list.
parent = (i - 1) // 2Who is above me?The list layout replaces pointers.
if h[parent] <= h[i]: breakParent already smaller: done.The heap rule holds again.
h[parent], h[i] = h[i], h[parent]Swap upwards.One level per swap: at most log n swaps.

Watch it run. Push 1 into the heap [2, 5, 4, 9, 7].

Watch out for:

  • Sorting the list after every push. O(n log n) each time instead of O(log n).

B · Pop the smallest: sift down

What it doesTakes the top, moves the last item to the top, then swaps it with its smaller child until both children are bigger.
Picture itThe most urgent patient goes in; the last arrival is moved to the front seat, then steps back past anyone more urgent.
CostO(log n).

The code. Children of index i: 2i + 1 and 2i + 2. Always swap with the smaller child.

def heap_pop(h):
    top = h[0]
    last = h.pop()
    if h:
        h[0] = last
        i = 0
        while True:
            small = i
            for c in (2 * i + 1, 2 * i + 2):
                if c < len(h) and h[c] < h[small]:
                    small = c
            if small == i:
                break
            h[i], h[small] = h[small], h[i]
            i = small
    return top

Line by line.

LineWhat it doesWhy
h[0] = lastThe last item fills the hole at the top.Keeps the tree complete; now fix the order.
for c in (2 * i + 1, 2 * i + 2)Look at both children.Check c < len(h): a node near the bottom may have one child or none.
h[i], h[small] = h[small], h[i]Swap with the smaller child.Swapping with the bigger one would put it above a smaller sibling.

Watch it run. Pop from [1, 5, 2, 9, 7, 4].

Watch out for:

  • Swapping with the left child always. Swap with the smaller child, or the heap rule breaks.

C · A priority queue of tasks

What it doesPushes (priority, task) pairs; pops always return the smallest priority number first.
Picture itThe emergency room: whoever has the most urgent triage number is seen next, whenever they arrived.
CostO(log n) per push and pop.

The code. Python compares tuples element by element, so the priority decides.

import heapq

def serve(arrivals):
    waiting = []
    order = []
    for priority, name in arrivals:
        heapq.heappush(waiting, (priority, name))
    while waiting:
        priority, name = heapq.heappop(waiting)
        order.append(name)
    return order

Line by line.

LineWhat it doesWhy
heapq.heappush(waiting, (priority, name))The priority goes first in the tuple.Tuples compare their first elements first.
heapq.heappop(waiting)The smallest priority number comes out.Ties fall back to comparing the names.

Watch it run. Patients arrive: cough (3), heart (1), fracture (2), rash (3).

Watch out for:

  • Ties between items that can't be compared. If two priorities are equal Python compares the items next; add a counter (priority, n, item) when items aren't comparable.

6. How to remember it

Memory hook: "The emergency room"The most urgent is always next. Newcomers bubble up past anyone less urgent; when the top leaves, the last arrival takes the seat and sinks back to where they belong.

Picture

A waiting room shaped like a pyramid, the most urgent patient on the top chair.

Chant

"Push sifts up, pop sifts down, the top is always smallest."

Key rule

Children of i are 2i + 1 and 2i + 2; the parent is (i − 1) // 2.

7. Problems solved with it

Two easy problems where a heap keeps handing you the extreme value.

LC 1046 · Last Stone Weight

The problemSmash the two heaviest stones together each turn: equal stones vanish, otherwise the difference remains. Return the last stone's weight, or 0.
What it doesA max-heap (negated values) hands over the two heaviest each turn.
CostO(n log n).

The code. Python's heapq is a min-heap, so store negatives.

import heapq

def last_stone_weight(stones):
    h = [-s for s in stones]
    heapq.heapify(h)
    while len(h) > 1:
        a = -heapq.heappop(h)
        b = -heapq.heappop(h)
        if a != b:
            heapq.heappush(h, -(a - b))
    return -h[0] if h else 0

Line by line.

LineWhat it doesWhy
h = [-s for s in stones]Negate so the heaviest is the smallest.A min-heap of negatives is a max-heap.
a = -heapq.heappop(h)The heaviest stone.Negate back when reading.
heapq.heappush(h, -(a - b))What's left goes back in.

Watch it run. [2, 7, 4, 1, 8, 1].

Watch out for:

  • Re-sorting the whole list each turn. O(n² log n) in total. The heap keeps each turn O(log n).

LC 703 · Kth Largest Element in a Stream

The problemNumbers arrive one by one. After each, report the k-th largest seen so far.
What it doesA min-heap holding only the k largest; its top is the answer.
CostO(log k) per number.

The code. Push each number; if the heap grows past k, pop the smallest.

import heapq

def kth_largest_stream(k, nums):
    h = []
    out = []
    for x in nums:
        heapq.heappush(h, x)
        if len(h) > k:
            heapq.heappop(h)
        out.append(h[0])
    return out

Line by line.

LineWhat it doesWhy
if len(h) > k: heapq.heappop(h)Too many? Drop the smallest.The heap keeps exactly the k largest so far.
out.append(h[0])The smallest of the k largest is the k-th largest.

Watch it run. k = 3; numbers 4, 5, 8, 2, 3, 5, 10, 9, 4.

Watch out for:

  • A max-heap of everything. Works, but uses O(n) memory and O(k log n) per answer.

More practice, solved on the pattern pages

#ProblemLevelWhere
215Kth Largest Element in an ArrayMediumSolved step by step on 07 · Top 'K' Elements
347Top K Frequent ElementsMediumSolved step by step on 07 · Top 'K' Elements
973K Closest Points to OriginMediumSolved step by step on 07 · Top 'K' Elements
23Merge k Sorted ListsHardSolved step by step on 07 · Top 'K' Elements
295Find Median from Data StreamHardSolved step by step on 07 · Top 'K' Elements

8. Common mistakes

Forgetting heapq is a min-heapFor the largest, push negatives and negate when you pop.
Treating the list as sortedOnly h[0] is guaranteed. h[1] is not necessarily the second smallest.
Comparing items that can't be comparedEqual priorities make Python compare the next tuple element. Add a tie-breaking counter.
Searching inside a heapFinding or removing an arbitrary item is O(n). If you need that, mark items as deleted and skip them when popped ("lazy deletion").
Pushing one at a time to build a heapn pushes cost O(n log n); heapify builds it in O(n).

9. Self-check quiz

Popping the smallest item from a heap of n items costs…

Sift down walks one root-to-leaf path of a tree with log n levels.

In a heap stored as a list, where are the children of index 3?

2 × 3 + 1 = 7 and 2 × 3 + 2 = 8.

How do you get a max-heap from Python's heapq?

The smallest negative is the largest number.

What does a min-heap guarantee about its list order?

Each parent ≤ its children; the top is the minimum, the rest is only partly ordered.

10. Where it shows up next

Next structure: 09 · Trees and binary trees. You've already met one: a heap is a binary tree in disguise.