Algorithm Search⌘K

Proofs: Floyd's heap construction

These are the human proofs of Floyd's heap construction, at full length.

Floyd's method uses fewer than n swaps and 2n comparisons full

This proof shows: time.worst, time.best, time.average, space.extra.

Let n≥1n \ge 1. For a node ii, its height h(i)h(i) is the largest h≥0h \ge 0 such that 2hi≤n2^h i \le n. We write T(i)T(i) for the set of nodes of the subtree of ii.

Lemma 1. h(i)h(i) is the largest depth of a node of T(i)T(i) below ii.

Proof. The nodes of T(i)T(i) at depth dd below ii are the indices 2di,2di+1,…,2di+2d−12^d i, 2^d i + 1, \ldots, 2^d i + 2^d - 1 that are at most nn, by induction on dd: the children of the indices 2di2^{d} i to 2di+2d−12^d i + 2^d - 1 are the indices 2d+1i2^{d+1} i to 2d+1i+2d+1−12^{d+1} i + 2^{d+1} - 1. So there is a node at depth dd exactly when the smallest of them, 2di2^d i, is at most nn. □\square

Lemma 2. MAX-HEAPIFY(A,n,i)(A, n, i) does at most h(i)h(i) swaps and at most 2h(i)2h(i) key comparisons.

Proof. Let i0i_0 be the first value of ii, so the claim is about h(i0)h(i_0). Each swap moves ii one level down inside T(i0)T(i_0), so by Lemma 1 there are at most h(i0)h(i_0) swaps. Each iteration does at most 2 comparisons (lines L5 and L6), and an iteration at a leaf does none, because both tests l≤nl \le n and r≤nr \le n fail. Let ss be the number of swaps. There are s+1s + 1 iterations. If the last one is at a leaf, the comparisons are at most 2s≤2h(i0)2s \le 2h(i_0). Else the last node has a child, so it is at depth s≤h(i0)−1s \le h(i_0) - 1, and the comparisons are at most 2(s+1)≤2h(i0)2(s + 1) \le 2h(i_0). □\square

Lemma 3. ∑i=1nh(i)=n−s2(n)\sum_{i=1}^{n} h(i) = n - s_2(n), where s2(n)s_2(n) is the number of 1 bits of nn.

Proof. By the definition, h(i)≥hh(i) \ge h exactly when 2hi≤n2^h i \le n, that is, when i≤⌊n/2h⌋i \le \lfloor n/2^h \rfloor. So, counting each ii once for each hh with 1≤h≤h(i)1 \le h \le h(i),

∑i=1nh(i)=∑h≥1#{i:h(i)≥h}=∑h≥1⌊n2h⌋.\sum_{i=1}^{n} h(i) = \sum_{h \ge 1} \#\{i : h(i) \ge h\} = \sum_{h \ge 1} \left\lfloor \frac{n}{2^h} \right\rfloor.

Write n=∑j=0Lbj2jn = \sum_{j=0}^{L} b_j 2^j with bj∈{0,1}b_j \in \{0, 1\}. Then ⌊n/2h⌋=∑j≥hbj2j−h\lfloor n/2^h \rfloor = \sum_{j \ge h} b_j 2^{j-h}, because the part ∑j<hbj2j\sum_{j < h} b_j 2^j is less than 2h2^h. Summing over h≥1h \ge 1 and exchanging the sums,

∑h≥1∑j≥hbj2j−h=∑j≥1bj∑h=1j2j−h=∑j≥0bj(2j−1)=n−∑jbj=n−s2(n).\sum_{h \ge 1} \sum_{j \ge h} b_j 2^{j-h} = \sum_{j \ge 1} b_j \sum_{h=1}^{j} 2^{j-h} = \sum_{j \ge 0} b_j (2^j - 1) = n - \sum_j b_j = n - s_2(n).

(The term j=0j = 0 adds b0(20−1)=0b_0 (2^0 - 1) = 0.) □\square

Theorem. BUILD-MAX-HEAP does at most n−s2(n)n - s_2(n) swaps and at most 2(n−s2(n))2(n - s_2(n)) key comparisons. Its time is Θ(n)\Theta(n) on each input of size nn, and its extra space is Θ(1)\Theta(1).

Proof. Line L1 calls MAX-HEAPIFY once for each i≤⌊n/2⌋i \le \lfloor n/2 \rfloor. The leaves i>⌊n/2⌋i > \lfloor n/2 \rfloor have h(i)=0h(i) = 0, so by Lemmas 2 and 3 the swaps are at most ∑ih(i)=n−s2(n)\sum_i h(i) = n - s_2(n) and the comparisons at most 2(n−s2(n))2(n - s_2(n)). Each call costs O(1)O(1) plus O(1)O(1) for each iteration, and each iteration other than the last does one swap. So the time is O(⌊n/2⌋+n−s2(n))=O(n)O(\lfloor n/2 \rfloor + n - s_2(n)) = O(n). Line L1 does ⌊n/2⌋\lfloor n/2 \rfloor iterations on every input, so the time is Ω(n)\Omega(n) in the best case too. The method keeps only the indices ii, ll, rr and mm and one key for a swap. ■\blacksquare

Floyd's method builds a max-heap from the keys of the input full

This proof shows: correctness, termination.

Write T(i)T(i) for the set of indices in the subtree of node ii in the complete binary tree on {1,…,n}\{1, \ldots, n\}: ii, its children 2i2i and 2i+12i + 1 when they are at most nn, their children, and so on. Say that T(i)T(i) is a heap when A[⌊j/2⌋]≥A[j]A[\lfloor j/2 \rfloor] \ge A[j] for each j∈T(i)j \in T(i) with j≠ij \ne i. Each index in T(i)T(i) other than ii is larger than ii, because a child has a larger index than its parent.

Lemma (MAX-HEAPIFY). Let 1≤i≤n1 \le i \le n, and assume that T(2i)T(2i) and T(2i+1)T(2i+1) are heaps (when they exist). Then MAX-HEAPIFY(A,n,i)(A, n, i) stops, it permutes the keys of the cells of T(i)T(i) and changes no other cell, and afterwards T(i)T(i) is a heap.

Proof. Let i0i_0 be the first value of ii. We show that this statement is a loop invariant of line L3, for the current value of ii:

(D) i∈T(i0)i \in T(i_0); each edge (⌊j/2⌋,j)(\lfloor j/2 \rfloor, j) with j∈T(i0)∖{i0}j \in T(i_0) \setminus \{i_0\} and ⌊j/2⌋≠i\lfloor j/2 \rfloor \ne i holds; and if i≠i0i \ne i_0, then A[⌊i/2⌋]≥A[c]A[\lfloor i/2 \rfloor] \ge A[c] for each child cc of ii with c≤nc \le n.

Initialization. i=i0i = i_0. The edges inside T(i0)T(i_0) that do not start at i0i_0 are the edges inside T(2i0)T(2i_0) and T(2i0+1)T(2i_0+1), and these hold because the two subtrees are heaps. The last part is empty.

Maintenance. Lines L4 to L6 set mm to an index of a largest key among A[i]A[i], A[l]A[l] (if l≤nl \le n) and A[r]A[r] (if r≤nr \le n): after line L5, A[m]=max⁡(A[i],A[l])A[m] = \max(A[i], A[l]), and after line L6, A[m]A[m] is also at least A[r]A[r]. If m=im = i, then each edge out of ii holds, so with (D) each edge inside T(i0)T(i_0) holds, and line L7 returns with T(i0)T(i_0) a heap. Else mm is a child of ii, y=A[m]>A[i]=xy = A[m] > A[i] = x, and y≥A[s]y \ge A[s] for the other child ss of ii, if it exists. Line L8 swaps xx and yy. Then:

  • the edge (i,m)(i, m) holds, because y>xy > x;
  • the edge (i,s)(i, s) holds, because y≥A[s]y \ge A[s];
  • if i≠i0i \ne i_0, the edge (⌊i/2⌋,i)(\lfloor i/2 \rfloor, i) holds, because the last part of (D) gave A[⌊i/2⌋]≥A[m]=yA[\lfloor i/2 \rfloor] \ge A[m] = y;
  • for each child cc of mm, the edge (m,c)(m, c) held before the swap, because (D) excluded only the edges out of ii, so y≥A[c]y \ge A[c], and yy is now at i=⌊m/2⌋i = \lfloor m/2 \rfloor.

The edges out of mm may now fail, and (D) for the new value i=mi = m excludes exactly them. The last point gives the last part of (D) for mm. Also m∈T(i0)m \in T(i_0). So line L9 keeps (D).

Termination. Each iteration that does not return replaces ii by a child, so the depth of ii below i0i_0 grows by exactly 1. The depth is at most the height of i0i_0, so there are at most that many swaps, and then line L7 returns, since a leaf gives m=im = i. Each change is a swap of two cells of T(i0)T(i_0). □\square

Theorem. BUILD-MAX-HEAP(A,n)(A, n) stops, and afterwards A[1…n]A[1 \ldots n] is a permutation of its input and satisfies A[⌊j/2⌋]≥A[j]A[\lfloor j/2 \rfloor] \ge A[j] for each 2≤j≤n2 \le j \le n.

Proof. We show this invariant of the loop of line L1:

(B) before the iteration for ii, the subtree T(j)T(j) is a heap for each jj with i<j≤ni < j \le n.

Initialization. The first value is i=⌊n/2⌋i = \lfloor n/2 \rfloor. Each j>⌊n/2⌋j > \lfloor n/2 \rfloor has 2j>n2j > n, so it has no child, and T(j)={j}T(j) = \{j\} is a heap. If n≤1n \le 1 the loop does not run, and the array is a heap.

Maintenance. Let i≥1i \ge 1. The children 2i2i and 2i+12i + 1 are larger than ii, so by (B) their subtrees are heaps. The lemma applies: after line L2, T(i)T(i) is a heap, and only cells of T(i)T(i) changed. Take jj with i−1<j≤ni - 1 < j \le n. If j=ij = i, T(j)T(j) is a heap. If j∈T(i)j \in T(i), then T(j)⊆T(i)T(j) \subseteq T(i), and a subset of a heap of this form is a heap, because its edges are edges of T(i)T(i). Else T(j)T(j) and T(i)T(i) are disjoint: jj is not an ancestor of ii, because an ancestor of ii has an index smaller than ii. So no cell of T(j)T(j) changed, and T(j)T(j) is still a heap. This is (B) for i−1i - 1.

Termination. The loop runs ⌊n/2⌋\lfloor n/2 \rfloor times, and each call stops by the lemma. After the iteration for i=1i = 1, (B) with i=0i = 0 says that T(1)={1,…,n}T(1) = \{1, \ldots, n\} is a heap. Each change is a swap, so AA is a permutation of its input. ■\blacksquare

Back to Floyd's heap construction