Heap Sort
Heap Sort gives you everything you wish quicksort had: O(n log n) worst case and O(1) extra space, all in one algorithm.
The catch? Worse constant factors than quicksort, no cache locality, and unstable. It’s the “no surprises” sort — perfect when worst-case guarantees matter, less common in everyday code.
The big idea
A heap is a binary tree (stored as an array) where every parent is >= its children (a max-heap). The maximum element is always at the root.
Heap Sort exploits this in two phases:
- Build a max-heap from the input array in O(n).
- Repeatedly swap the root with the last element, shrink the heap by one, and sift down to restore the heap property. After
n - 1iterations, the array is sorted in ascending order.
We’ll spend Day 7 in detail on heaps. For now, here’s the minimum you need to follow Heap Sort.
Heap as array (the trick)
A binary heap is stored as a flat array using these index relations:
parent(i) = (i - 1) / 2
left(i) = 2 * i + 1
right(i) = 2 * i + 2No pointers, no nodes — just arithmetic. This is what makes heap sort in-place: the “tree” lives entirely in the same array we’re sorting.
Heap [16, 14, 10, 8, 7, 9, 3, 2, 4, 1] as a tree:
16
/ \
14 10
/ \ / \
8 7 9 3
/ \ /
2 4 1The max is at index 0. Children of index 0 are at indices 1 and 2. Children of index 1 are at 3 and 4. And so on.
The “sift down” subroutine
To restore the heap property at index i after the root is replaced:
- Find the larger of
a[i]’s two children. - If that child is larger than
a[i], swap them. - Recursively repeat at the child’s position.
This takes O(log n) per call — the height of the heap.
The full algorithm
The sneaky build-heap O(n)
You’d think building a heap is O(n log n) — n calls to siftDown, each O(log n). But it’s actually O(n).
The reason: most nodes are near the leaves, and sifting down from a leaf is O(1). Only the root pays the full O(log n). The total work is bounded by:
∑ (n / 2^(h+1)) · h ≤ 2nThis is one of the most surprising “tight bound” results in basic algorithms — and a common interview question.
The full algorithm is O(n) build + O(n log n) extractions = O(n log n).
Properties
- Time: O(n log n) — best, average, and worst case. No bad pivots, no surprises.
- Space: O(1) — fully in-place. The “tree” is just the input array.
- Stable: No. Swaps during heapify can reorder equal elements.
- Adaptive: No.
When to actually use heap sort
In raw speed, quicksort beats heap sort by ~2× because of cache behavior — heap operations jump around the array in a way that defeats CPU prefetching. But heap sort wins in three specific scenarios:
- Worst-case guarantees matter. Real-time systems, security-sensitive code (you don’t want an adversary picking inputs that trigger quicksort’s O(n²)), embedded systems with tight deadlines.
- Tight memory. Heap sort is O(1) extra space. Merge sort is O(n). On a microcontroller with 8 KB of RAM, that gap is everything.
- The fallback for hybrid sorts. Introsort uses heap sort as the fallback when quicksort recursion gets too deep. You don’t have to write this — but knowing why is useful.
The bigger picture
Heap sort isn’t usually the right answer for “sort this array.” But the heap data structure itself — independent of sorting — is one of the most important structures in computer science. We use it for:
- Priority queues (the canonical use)
- Dijkstra’s algorithm (Day 21)
- A* search
- Top-K problems (covered in Top K Frequent Elements)
- Median maintenance (two heaps trick)
- Event scheduling, Huffman coding, and more
We’ll go deeper on heaps in Day 7. For now, you have the rough idea — and one of the cleanest O(n log n) sorts in your toolbox.