Memra

Heaps & heapsort

◈ 6 cards

The array-as-tree heap, MAX-HEAPIFY in O(lg n), the surprising O(n) build, and an in-place O(n lg n) sort.

A tree that lives in an array

A (binary max-)heap is an array that we read as a nearly-complete binary tree. No pointers: the shape is encoded by arithmetic on indices. Using 1-based CLRS indexing, for node :

  • parent is at ,
  • left child at , right child at .

The max-heap property is the single local rule for every non-root . It says nothing about siblings — only parent-vs-child. Its one powerful consequence: the largest element is always at the root . Because the tree is nearly complete, its height is , so any root-to-leaf walk does work.

MAX-HEAPIFY — sink one bad node

MAX-HEAPIFY(A, i) assumes the subtrees rooted at 's children are already heaps but may be too small. It finds the largest of , , ; if a child wins, it swaps and recurses into that child — the small value floats down at most levels. The recurrence is (a child holds at most nodes when the bottom level is half full), which the master theorem solves to .

BUILD-MAX-HEAP — and why it is , not

Call MAX-HEAPIFY bottom-up, from the last internal node down to 1. The leaves are already size-1 heaps, so we skip them; going downward guarantees both children are heaps before we fix a node. The loose bound is calls but it is not tight. Most nodes sit near the leaves, where MAX-HEAPIFY is cheap. There are at most nodes of height , and MAX-HEAPIFY costs on a height- node, so the total is

using at . Building a heap is linear.

Worked example — HEAPSORT

Heapsort has two phases. (1) BUILD-MAX-HEAP puts the max at in . (2) For from downto : swap (the max takes its final sorted slot), shrink the heap by one, and MAX-HEAPIFY the new root. Each of the sift-downs is , so heapsort is in every case, and it is in-place ( extra). It is not stable.

Trace BUILD-MAX-HEAP on (1-based): start at index 5, work down, and cascading sift-downs yield the heap . The runnable exercises reproduce exactly this.

12345678910A1614108793241i = 22i = 42i+1 = 5PARENT(i) = ⌊i/2⌋, LEFT = 2i, RIGHT = 2i+1.
The heap as an array. Node 2 holds 14; its children are at 2i = 4 and 2i+1 = 5 — no pointers, just arithmetic.
16i=114i=28i=42i=84i=97i=51i=1010i=39i=63i=7
The same ten values as a tree. The array and the tree are one structure read two ways — every parent is ≥ both children, so the maximum is at A[1].
12345678910A (input)4132169101487⌊n/2⌋ = 5leaves — already heapsA (heap)1614108793241maxMAX-HEAPIFY runs bottom-up from ⌊n/2⌋ down to 1.
Why the build is linear: the bracketed half are already heaps and are skipped, and the nodes we do fix are mostly near the leaves, where a sift-down is cheap.
NORMAL ~/memra/learn/comp-372/heaps-and-heapsort utf-8 LF