Algorithm Search⌘K

Proofs: Binary heap

These are the human proofs of Binary heap, at full length.

The operations of a binary heap keep the heap invariant and return a minimum full

This proof shows: correctness, termination.

We write nn for H.nH.n, p(j)=⌊j/2⌋p(j) = \lfloor j/2 \rfloor for the parent of j≥2j \ge 2, and C(j)C(j) for the set of children of jj that exist, a subset of {2j,2j+1}\{2j, 2j+1\} that holds only indices at most nn. An edge is a pair (p(j),j)(p(j), j) with 2≤j≤n2 \le j \le n, and it holds when A[p(j)]≤A[j]A[p(j)] \le A[j]. The invariant (H) says that each edge holds. Each operation changes AA only by swaps and by the writes of lines L12, L18 and L23, which we follow one by one.

Lemma 1 (sift-up). Let 1≤i≤n1 \le i \le n. Assume

(U) each edge (p(j),j)(p(j), j) with j≠ij \ne i holds, and if i≥2i \ge 2, then A[p(i)]≤A[c]A[p(i)] \le A[c] for each c∈C(i)c \in C(i).

Then SIFT-UP(A,i)(A, i) stops after at most ⌊lg⁡i⌋\lfloor \lg i \rfloor iterations, permutes the keys of A[1…n]A[1 \ldots n], and after it (H) holds.

Proof. We show that (U) is a loop invariant of line L1 for the current ii. It holds at the start by assumption.

Maintenance. Let the test of line L1 be true, so i≥2i \ge 2 and, with q=p(i)q = p(i), x=A[q]>A[i]=yx = A[q] > A[i] = y. Line L2 swaps them, so afterwards A[q]=yA[q] = y and A[i]=xA[i] = x. We check each edge that has qq or ii as an end, other than the edge into qq, which (U) for the new value i=qi = q excludes.

  • The edge (q,i)(q, i): y≤xy \le x, since y<xy < x.
  • The edges (i,c)(i, c) for c∈C(i)c \in C(i): the second part of (U) gave x=A[q]≤A[c]x = A[q] \le A[c] before the swap, and A[c]A[c] did not change.
  • The edge (q,s)(q, s) for the other child ss of qq, if it exists: the edge held before, so x≤A[s]x \le A[s], and now A[q]=y<x≤A[s]A[q] = y < x \le A[s].

No other edge has an end at qq or ii, except the edge (p(q),q)(p(q), q) when q≥2q \ge 2. So after line L3 sets i←qi \leftarrow q, each edge except the one into the new ii holds. For the second part of (U) at the new i=q≥2i = q \ge 2: the edge (p(q),q)(p(q), q) held before the swap, because (U) excluded only the edge into the old ii, so A[p(q)]≤xA[p(q)] \le x. The children of qq now hold xx (at the old ii) and A[s]≥xA[s] \ge x. So A[p(q)]A[p(q)] is at most each child of qq.

Termination. Each iteration replaces ii by ⌊i/2⌋\lfloor i/2 \rfloor, so ⌊lg⁡i⌋\lfloor \lg i \rfloor drops by exactly 1, and the test i>1i > 1 fails after at most ⌊lg⁡i⌋\lfloor \lg i \rfloor iterations. When the loop stops, either i=1i = 1, and then (U) says that each edge holds because the root has no edge into it, or A[p(i)]≤A[i]A[p(i)] \le A[i], and then the one excluded edge holds too. In both cases (H) holds. Each change is a swap, so the keys are permuted. □\square

Lemma 2 (sift-down). Let 1≤i≤n1 \le i \le n. Assume

(D) each edge (p(j),j)(p(j), j) with p(j)≠ip(j) \ne i holds, and if i≥2i \ge 2, then A[p(i)]≤A[c]A[p(i)] \le A[c] for each c∈C(i)c \in C(i).

Then SIFT-DOWN(A,n,i)(A, n, i) stops after at most hh swaps, where h=⌊lg⁡(n/i)⌋h = \lfloor \lg(n/i) \rfloor is the height of ii, permutes the keys of A[1…n]A[1 \ldots n], changes only the cells of the subtree of ii, and after it (H) holds.

Proof. (D) is a loop invariant of line L4. It holds at the start by assumption. In an iteration, lines L5 to L7 set mm to an index of a smallest key among A[i]A[i] and A[c]A[c] for c∈C(i)c \in C(i): line L6 compares A[2i]A[2i] with A[i]A[i], and line L7 compares A[2i+1]A[2i+1] with the smaller of the two, and each test is done only for a child that exists.

Stop. If m=im = i, then A[i]≤A[c]A[i] \le A[c] for each c∈C(i)c \in C(i), so the edges out of ii hold, and with (D) each edge holds: (H) is true, and line L8 returns.

Maintenance. If m≠im \ne i, let x=A[i]x = A[i] and y=A[m]y = A[m], so y<xy < x and y≤A[s]y \le A[s] for the other child ss of ii. Line L9 swaps them. We check the edges with an end at ii or mm, except the edges out of mm, which (D) for the new value i=mi = m excludes.

  • The edge (i,m)(i, m): A[i]=y≤x=A[m]A[i] = y \le x = A[m].
  • The edge (i,s)(i, s): A[i]=y≤A[s]A[i] = y \le A[s].
  • The edge (p(i),i)(p(i), i) if i≥2i \ge 2: the second part of (D) gave A[p(i)]≤yA[p(i)] \le y, because m∈C(i)m \in C(i), and A[i]A[i] is now yy.

For the second part of (D) at the new i=mi = m: its parent is the old ii, which now holds yy, and each child cc of mm had y=A[m]≤A[c]y = A[m] \le A[c] before the swap, because the edges out of mm were not excluded by (D). So line L10 keeps (D).

Termination. Each swap moves ii to a child, so the depth of ii grows by exactly 1. A node of the subtree of the first ii has depth at most hh below it, so there are at most hh swaps, and after them the next iteration stops at line L8 or finds no child. The changed cells are ii and its descendants. □\square

Insert. Before line L11, (H) holds on A[1…n]A[1 \ldots n]. Lines L11 and L12 add the cell n+1n + 1 with the key kk. Each old edge holds, and the new node has no children. So (U) holds with i=n+1i = n + 1, and Lemma 1 gives (H) on the new heap, whose keys are the old keys and kk.

Find-min. By (H) and the global form of the heap property, A[1]≤A[j]A[1] \le A[j] for each j≤nj \le n, because the root is an ancestor of each node. So line L15 returns a minimum. Nothing changes.

Extract-min. Let n≥1n \ge 1 and let μ=A[1]\mu = A[1], a minimum by the claim on find-min. Lines L18 and L19 write the last key A[n]A[n] into the root and drop the cell nn, so the keys of A[1…n−1]A[1 \ldots n-1] are the old keys with one copy of μ\mu removed. If n−1=0n - 1 = 0, the heap is empty and (H) holds. Else each edge with p(j)≠1p(j) \ne 1 is an old edge between two unchanged cells, so it holds, and the root has no parent. So (D) holds with i=1i = 1, and Lemma 2 gives (H). Line L21 returns μ\mu.

Decrease-key. Let k≤A[i]k \le A[i], as line L22 checks, and let a=A[i]a = A[i] before line L23. Each edge not into ii and not out of ii is unchanged. The edges (i,c)(i, c) held with aa, and k≤ak \le a, so they still hold. If i≥2i \ge 2, then A[p(i)]≤a≤A[c]A[p(i)] \le a \le A[c] for each c∈C(i)c \in C(i), by the old edges. So (U) holds, and Lemma 1 gives (H). The keys are the old keys with aa replaced by kk.

Build. We claim that, before the iteration of line L26 for ii, the subtree of each index j>ij > i is a heap, that is, each edge inside that subtree holds. At the start i=⌊n/2⌋i = \lfloor n/2 \rfloor, and each j>⌊n/2⌋j > \lfloor n/2 \rfloor is a leaf, whose subtree has no edges. In the iteration for ii, the subtrees of the children of ii have indices larger than ii, so they are heaps. Consider the subtree of ii alone as a heap with root ii: the only edges that may fail are the ones out of ii, and ii has no parent inside it. So (D) holds for it, and by Lemma 2 the subtree of ii becomes a heap. Now take an index j>ij > i, so that the claim must hold for the next iteration. If jj is in the subtree of ii, its subtree is part of a heap, so it is a heap. Else the subtree of jj is disjoint from the subtree of ii: an index j>ij > i is not an ancestor of ii, because an ancestor has a smaller index. Lemma 2 says that only cells of the subtree of ii change, so the subtree of jj is unchanged and stays a heap. When the loop ends with i=1i = 1, the subtree of 11 is the whole tree, and (H) holds. Each step is a swap, so the keys of AA are the keys of BB.

Theorem. If (H) holds before an operation, it holds after it; FIND-MIN and EXTRACT-MIN return a minimum key of the heap; and each operation changes the multiset of keys exactly as the priority queue type says (insert adds kk, extract-min removes one minimum, decrease-key replaces the key at ii by kk, build makes the multiset of BB). Each operation stops.

Proof. The claims above, for each operation, together with Lemmas 1 and 2. ■\blacksquare

Back to Binary heap