3.6.5 · D5Sorting & Searching
Question bank — Heap sort — O(n log n), in-place, not stable
Before we start, two words we lean on constantly:
True or false — justify
A max-heap stored in an array is a sorted array.
False. The heap property only says parent ≥ children, a local rule. Siblings can be in any order, so e.g.
[9, 3, 8, 1, 2, 7] is a valid heap but not sorted.The largest element of a max-heap is always at the root (index 0).
True. If every parent ≥ its children, then by chaining that inequality up any path, the root dominates every node — it is the global maximum.
The smallest element of a max-heap is always at the last array position.
False. The minimum is always a leaf (a node with no children can't be forced down by a parent), but it can be any leaf, not necessarily the last one.
Heap sort is stable — equal keys keep their original order.
False. Swapping the root to the back and sifting reorders equal keys unpredictably; e.g. two 5's can have their relative order flipped, so heap sort is not stable.
Heap sort's worst case is like quicksort's.
False. Every
sift-down costs at most because a complete tree has height , and we do of them — so all cases (best, average, worst) are .Heap sort needs extra memory proportional to .
False. All swaps happen inside the one array; the tree is implied by index formulas, never materialised. So it is in-place, using only extra space.
Building the heap costs .
False. Naïvely it looks like sift-downs × , but most nodes are near the bottom with tiny subtrees. Summing the real work gives for build-heap — the sort-down phase is what makes the whole thing .
Every complete binary tree is a heap.
False. "Complete" is only about shape (levels filled left-to-right). The heap property about values is a separate condition that must also hold.
Reversing a max-heap array gives a min-heap.
False. Reversing the array scrambles the parent–child index relationships (
2i+1, 2i+2 no longer point to the same conceptual nodes), so the result is generally neither a valid min-heap nor even a valid heap.In the sort-down phase, the heap region and the sorted region live in the same array.
True. The front
A[0..end-1] is the shrinking heap; the tail A[end..n-1] holds crowned maxima in final sorted position. One array, two zones — that's why it's in-place.Spot the error
sift_down should always swap the parent with its left child first. — What's wrong?
You must swap with the larger of the two children. Swapping with the left child even when the right is bigger can leave the right child larger than its new parent, violating the heap property.
Build-heap by calling sift_down on nodes 0, 1, 2, …, n-1 in order. — What's wrong?
You must go from the last internal node down to the root (
n//2 - 1 down to 0). sift-down assumes both subtrees are already heaps, which is only true if you process children before parents — i.e. bottom-up.To build a heap, run sift_down on every index including the leaves. — What's wrong?
Leaves (indices
n//2 … n-1) have no children, so they're trivially heaps already. Sifting them is wasted work; we start at n//2 - 1, the last node that has a child.In the sort loop, after swap(A[0], A[end]) call sift_down(A, 0, n). — What's wrong?
It must be
sift_down(A, 0, end). The just-crowned maximum sits at A[end] and is now sorted/frozen; passing n would drag it back into the heap and undo the work.sift_down can stop as soon as it finds one child that is smaller than the parent. — What's wrong?
You must compare against both children (pick the larger) before deciding. Stopping early can leave the other, larger child above its parent — heap property broken.
The parent of node i is i/2 for a 0-indexed array. — What's wrong?
For 0-indexing the parent is . Plain
i/2 is the 1-indexed formula; using it here mislinks nodes and corrupts the heap.Why questions
Why does heap sort re-heapify only the root after each swap, not rebuild the whole heap?
Because before the swap the whole region was already a valid heap; the swap only disturbs one node — the new root. Its two subtrees are still valid, so a single
sift_down from the root restores everything in .Why do we crown the max to the back of the array rather than the front?
The back is where the final sorted position of the current maximum belongs (largest goes last). Placing it there lets us shrink the heap by one and never touch that slot again — building the sorted zone from right to left for free.
Why does the array-index trick (2i+1, 2i+2) require the tree to be complete?
A complete tree has no gaps when numbered level-by-level, so the indices pack tightly with no wasted slots. Any gap would break the doubling formula and force us to store pointers, killing the in-place property.
Why is heap sort's time the same in best, average, and worst case, unlike quicksort?
There's no pivot to get unlucky with. The tree height is fixed at regardless of input order, so each of the sift-downs is bounded by every time — no input can trigger degeneracy.
Why does sift_down swap with the larger child specifically?
The larger child becomes the new parent, so it must dominate its sibling too. Promoting the smaller child could leave the larger one below it, immediately re-violating the heap property.
Why is heap sort not stable even though it only swaps within the array?
Stability needs equal keys to keep relative order, but the crown-to-back swap moves an element across the whole array, leaping over equal keys. Those long-range jumps scramble the original ordering of ties.
Why is build-heap but sort-down , given both call sift_down times?
In build-heap most nodes are near the bottom with short subtrees, so their sift-downs are cheap and the sum telescopes to . In sort-down every re-heapify starts at the root — full height each time — giving .
Edge cases
What does heap sort do with an empty array (n = 0)?
Nothing — both loops (
for i = n//2-1 … 0 and for end = n-1 … 1) don't execute. An empty array is already sorted, correctly handled by doing zero work.What about a single-element array (n = 1)?
Also a no-op:
n//2 - 1 = -1 so build-heap skips, and the sort loop runs end from 0 down to 1 (empty range). One element is trivially sorted.What happens if all elements are equal, e.g. [5, 5, 5, 5]?
The heap property (
parent ≥ child) holds everywhere with equality, so sift-down never swaps meaningfully; the array stays as-is and is (trivially) sorted. It runs in full but does minimal real work — and instability is invisible since keys are indistinguishable.For an already-sorted ascending array, is heap sort fast?
No — it's still . An ascending array is not a max-heap (the max is at the end, not the root), so build-heap and every sift-down do full work. Heap sort has no "already-sorted" shortcut.
What is the height of a heap with node, and how many sift-downs run to build it?
Height is (just a root).
n//2 - 1 = -1, so build-heap performs zero sift-downs — nothing to sink.If the last internal node has only a left child (odd-shaped last level), does sift_down still work?
Yes. The check
r < n fails for the missing right child, so only the left child is considered. The bounds test handles the incomplete last level automatically — no special case needed.Does sift_down ever need to sift a node up?
No — sift-down only pushes values toward the leaves. Building and re-heapifying are designed so the only out-of-place node can always be fixed by sinking it; upward movement (
sift-up) belongs to heap insertion, not to heap sort.Recall Quick self-check
Name the two array zones during sort-down, and which grows.
Answer ::: The heap zone A[0..end-1] (shrinks) and the sorted zone A[end..n-1] (grows), filling from the right.