Algorithm Search⌘K

Proofs: Heap-based selection

These are the human proofs of Heap-based selection, at full length.

Heap-based selection returns the k smallest items in sorted order full

This proof shows: correctness, termination.

Let the input be a0,…,an−1a_0, \ldots, a_{n-1}, let m=min⁡(k,n)m = \min(k, n), and let a(1)≤⋯≤a(n)a_{(1)} \le \cdots \le a_{(n)} be the input in sorted order. The claim is: HEAP-SELECT(a,k)(a, k) stops and returns the list a(1),…,a(m)a_{(1)}, \ldots, a_{(m)}.

A list H[0…s−1]H[0 \ldots s-1] is a max-heap when H[⌊(c−1)/2⌋]≥H[c]H[\lfloor (c-1)/2 \rfloor] \ge H[c] for each 1≤c<s1 \le c < s, that is, each item is at least each of its children. A max-heap of size s≥1s \ge 1 has its largest item at index 0, since each index is joined to 0 by a chain of parents along which the items do not decrease.

Lemma 1 (SIFT-DOWN). Let 0≤i<s0 \le i < s, and assume that the parent condition holds for each cc in the subtrees of the children of ii, that is, these two subtrees are max-heaps. Then SIFT-DOWN(H,i,s)(H, i, s) stops, permutes H[0…s−1]H[0 \ldots s-1] by swaps inside the subtree of ii, and after it the subtree of ii is a max-heap.

Proof. By induction on the height of ii in the tree. If ii has no child, line L13 returns and the subtree is the single item H[i]H[i]. Else line L14 picks cc as a child of ii with the larger item, so H[c]H[c] is the largest item of the two children, and by the heap order of the child subtrees it is the largest item of both subtrees. If H[i]≥H[c]H[i] \ge H[c], line L15 returns, and H[i]H[i] is at least each item below it, so the subtree is a max-heap. Else line L16 swaps H[i]H[i] and H[c]H[c]. Now H[i]H[i] is the largest item of the subtree, at least both children; the other child's subtree did not change; and the subtree of cc has the max-heap order below its children, so the loop continues at cc, which has a smaller height. By the induction hypothesis the subtree of cc is a max-heap at the end, and its items are the same as before as a multiset, so each of them is at most the new H[i]H[i]. Each pass moves ii down one level, so the loop stops. □\square

Lemma 2 (build). After line L2, H[0…m−1]H[0 \ldots m-1] is a max-heap and holds a0,…,am−1a_0, \ldots, a_{m-1}.

Proof. The indices i≥⌊m/2⌋i \ge \lfloor m/2 \rfloor have no child. Line L2 visits i=⌊m/2⌋−1,…,0i = \lfloor m/2 \rfloor - 1, \ldots, 0. Before the visit of ii, the subtrees of its children are max-heaps: their roots have larger indices than ii, so they were visited before, or they are leaves, and later visits of larger indices do not touch them again. By Lemma 1, after the visit the subtree of ii is a max-heap. After the visit of 00 the whole heap is a max-heap. Only swaps are made, so the items are those of line L1. □\square

Invariant of the loop of line L3. Write Pj=a0,…,aj−1P_j = a_0, \ldots, a_{j-1} for the prefix of the first jj items read. At each test of line L3, when jj items have been read:

(S) H[0…m−1]H[0 \ldots m-1] is a max-heap, and the multiset of its items equals the multiset of the mm smallest entries of a sorted order of PjP_j.

  1. Initialization. If k=0k = 0, m=0m = 0 and the heap is empty; if n≤kn \le k, m=nm = n and there is no later item. In the main case j=m=kj = m = k, and by Lemma 2 the heap holds all of PkP_k, which are its kk smallest entries.
  2. Maintenance. Let x=ajx = a_j be the next item, and let y=H[0]y = H[0], the largest candidate. Let s1≤⋯≤sjs_1 \le \cdots \le s_j be PjP_j sorted, so HH holds s1,…,sms_1, \ldots, s_m and y=smy = s_m.
    • If x≥yx \ge y, line L4 does nothing. A sorted order of Pj+1P_{j+1} is s1,…,sms_1, \ldots, s_m followed by the other items and xx in sorted order, since all of these are at least sms_m. Its first mm entries are the items of HH, so (S) holds.
    • If x<yx < y, lines L5 and L6 replace yy by xx. A sorted order of Pj+1P_{j+1} is obtained by inserting xx into s1,…,sjs_1, \ldots, s_j at a place before sms_m. Its first mm entries are xx and s1,…,sm−1s_1, \ldots, s_{m-1}, which is the new multiset of HH. The children of the root had max-heap subtrees before, and they did not change, so by Lemma 1 the heap is a max-heap after line L6.
  3. Termination of the loop. Each pass reads one item, and there are n−mn - m of them.

At the end of the loop j=nj = n, so by (S) the heap holds a(1),…,a(m)a_{(1)}, \ldots, a_{(m)} as a multiset.

The sort phase. At each test of line L7 with index jj, H[0…j]H[0 \ldots j] is a max-heap and H[j+1…m−1]H[j+1 \ldots m-1] holds the m−1−jm - 1 - j largest items of the heap in increasing order, each at least each item of H[0…j]H[0 \ldots j]. At the start j=m−1j = m - 1 and the second part is empty. Line L8 moves the largest item H[0]H[0] of the first part to index jj, which keeps the order of the second part, and line L9 restores the heap order of H[0…j−1]H[0 \ldots j-1] by Lemma 1, since the subtrees of the children of the root did not change. The loop runs m−1m - 1 times, and at the end H[0]H[0] is the smallest item and H[1…m−1]H[1 \ldots m-1] is increasing. So H=a(1),…,a(m)H = a_{(1)}, \ldots, a_{(m)} in this order, which line L10 returns. ■\blacksquare

Back to Heap-based selection