Skip to content

Trees and Graphs

Heaps and priority queues

Always reach the smallest thing fast

A complete tree that secretly lives in a flat array - giving you the smallest item on demand, and the arithmetic that makes pointers unnecessary.

Every scheduler, every shortest-path finder, every "give me the top 10" query has the same nagging need: repeatedly pull the smallest (or largest) thing out of a changing pile - fast. A sorted list makes that pull cheap but every insert expensive. A binary heap strikes the perfect bargain: to add, to remove the minimum, and to peek at it - all inside a plain flat array with no pointers at all.

The idea in one line

A min-heap is a complete binary tree (every level full, filled left-to-right) with one rule: every parent is both of its children. That rule alone forces the global minimum to sit at the root - so the smallest element is always one glance away.

A complete tree that hides in an array

Because the tree is complete, we never need pointers to wire nodes together. Just write the tree out level by level into an array, and the structure is encoded purely by index arithmetic. For the node stored at index :

That is the whole trick. "Go to my child" is a multiply-and-add; "go to my parent" is a subtract-and-halve. No allocation, no dereferencing - the array is the tree, and cache-friendly to boot. The heap property () is only a partial order: siblings have no defined relationship, which is exactly why a heap is cheaper to maintain than a fully sorted array.

See it: tree and array, sift up and sift down

Insert a value and watch it drop into the next free array slot, then sift up - swapping with its parent while it is smaller - in the tree and the array at once. Extract-min removes the root, drops the last element into its place, and sifts down. Click any node or cell to reveal its live / index math.

Min-heap: tree and array in lock-step. Insert, extract-min, or click a node to see its index math.
2051421239475206↑ min · peek is O(1)

Tree position i is exactly array index i - that correspondence is what makes children live at 2i+1 and 2i+2.

n = 7 · min = 2

Click any node or array cell to reveal its parent/child index math. Insert a value to watch it sift up; extract-min to watch it sift down.

Why both operations are O(log n)

A complete tree of nodes has height

Sift-up walks from a leaf toward the root; sift-down walks from the root toward a leaf. Either way you touch at most one node per level, doing a single compare-and-swap each time. So insert and extract-min each cost about swaps - while peek is a single array read, , because the minimum never leaves index 0.

Insert and extract-min in code

The entire data structure is one array plus two helpers. Notice there is not a single pointer or node object - every "move" is index arithmetic.

class MinHeap:
    def __init__(self):
        self.a = []                          # the backing array

    def push(self, x):
        self.a.append(x)                     # land in the next free slot
        self._sift_up(len(self.a) - 1)

    def pop_min(self):
        top = self.a[0]                     # the minimum - O(1) to read
        last = self.a.pop()
        if self.a:
            self.a[0] = last                # move last element to the root
            self._sift_down(0)
        return top

    def _sift_up(self, i):
        while i > 0:
            parent = (i - 1) // 2            # parent index
            if self.a[i] >= self.a[parent]:
                break                        # heap property holds
            self.a[i], self.a[parent] = self.a[parent], self.a[i]
            i = parent

    def _sift_down(self, i):
        n = len(self.a)
        while True:
            l, r, small = 2*i + 1, 2*i + 2, i   # children indices
            if l < n and self.a[l] < self.a[small]: small = l
            if r < n and self.a[r] < self.a[small]: small = r
            if small == i:
                break
            self.a[i], self.a[small] = self.a[small], self.a[i]
            i = small
One array, two helpers: sift up on push, sift down on pop. No pointers anywhere.

In real Python you would just reach for the standard library - import heapq gives you heappush and heappop over an ordinary list, implementing exactly this. Building it once by hand is what makes that black box transparent.

Where heaps quietly run the world

A heap is a priority queue. Dijkstra's shortest paths pull the nearest unvisited node with extract-min. Top-k ("10 largest of a billion") keeps a size-k heap and evicts the weakest in . OS schedulers and event simulations fire the highest-priority task next. And heapsort is just "insert everything, then extract-min until empty" giving a guaranteed sort in extra space.

A heap is not sorted

The heap property only relates parents to children, never siblings. So the backing array is partially ordered - you cannot binary-search it, and reading it left-to-right does not give sorted output. The only cheap guarantee is the one at the top: index 0 holds the minimum.

Check yourself

In an array-backed heap, the parent of the node at index i sits at index:

In a min-heap, where does the smallest element always live, giving O(1) peek?

Recall: why are insert and extract-min both O(log n), yet peek is only O(1)?

Tie the height of a complete tree to the cost of each operation. Try to state it, then check.

Lock it in

  • A min-heap is a complete binary tree with one rule: every parent is at most its children, so the global minimum sits at the root.
  • The tree lives in a flat array by pure index arithmetic: children at 2i+1 and 2i+2, parent at floor((i-1)/2). No pointers, cache-friendly.
  • Insert sifts up, extract-min sifts down; both touch one node per level, so both are O(log n) while peek is O(1).
  • A heap is only partially ordered: index 0 is the minimum, but the array is not sorted and cannot be binary-searched.

Primary source

Watch a heap grow and shrink node by node on VisuAlgo - Binary Heap, then compare its animation of sift-up and sift-down against the demo above. The USF Data Structure Visualizations (David Galles) include a step-through heap that also renders the tree and its array side by side.

Ask your teacher

Want to see heapify build a heap in (not ), or how a d-ary or Fibonacci heap changes the trade-offs? Ask me. Here is the connection worth holding onto: the priority queue behind Dijkstra's shortest paths is the same machinery as beam search in LLM decoding - both keep the best- candidates on a heap, popping the strongest and pushing new ones. That bridge is traced in priority queues and Dijkstra to beam search in the LLM course.