Algorithm Search⌘K

Proofs: Quickselect

These are the human proofs of Quickselect, at full length.

Quickselect returns the k-th smallest item full

This proof shows: correctness, termination.

Let SS be the multiset of the items of the input, and let s0≤s1≤⋯≤sn−1s_0 \le s_1 \le \cdots \le s_{n-1} be SS in sorted order. The claim is: for 1≤k≤n1 \le k \le n, QUICKSELECT(A,k)(A, k) stops and returns sk−1s_{k-1}, and at the end AA is a permutation of SS with A[k−1]=sk−1A[k-1] = s_{k-1}, each item of A[0…k−2]A[0 \ldots k-2] at most sk−1s_{k-1}, and each item of A[k…n−1]A[k \ldots n-1] at least sk−1s_{k-1}.

Lemma (PARTITION). Assume hi−lo≥1hi - lo \ge 1, and let xx be the item A[hi−1]A[hi-1] at the start. PARTITION(A,lo,hi)(A, lo, hi) permutes A[lo…hi−1]A[lo \ldots hi-1], changes no other entry, and returns pp with lo≤p<hilo \le p < hi, A[p]=xA[p] = x, each item of A[lo…p−1]A[lo \ldots p-1] at most xx, and each item of A[p+1…hi−1]A[p+1 \ldots hi-1] greater than xx.

Proof. Line L10 sets xx. The loop of line L12 keeps this invariant at the start of the iteration for jj: lo≤i≤jlo \le i \le j, each item of A[lo…i−1]A[lo \ldots i-1] is at most xx, each item of A[i…j−1]A[i \ldots j-1] is greater than xx, and A[hi−1]=xA[hi-1] = x.

  1. Initialization. Line L11 sets i=loi = lo and the first value is j=loj = lo, so both parts are empty.
  2. Maintenance. If A[j]>xA[j] > x, the loop only increases jj, so A[j]A[j] joins the part of the greater items. If A[j]≤xA[j] \le x, line L14 swaps A[j]A[j] with A[i]A[i], which is the first item of the greater part (or A[j]A[j] itself if that part is empty). Then A[i]≤xA[i] \le x and the greater part moves one place to the right, so after line L15 both statements hold for j+1j + 1. The index jj stays below hi−1hi - 1, so A[hi−1]A[hi - 1] does not change.
  3. Termination. The loop runs for j=lo,…,hi−2j = lo, \ldots, hi - 2, so it ends with j=hi−1j = hi - 1. Then A[lo…i−1]≤xA[lo \ldots i-1] \le x and A[i…hi−2]>xA[i \ldots hi-2] > x. Line L16 swaps A[i]A[i] with A[hi−1]=xA[hi-1] = x. If i<hi−1i < hi - 1, the item moved to hi−1hi - 1 is greater than xx, so A[i+1…hi−1]>xA[i+1 \ldots hi-1] > x. If i=hi−1i = hi - 1, the swap does nothing. In both cases the lemma holds with p=ip = i.

Each step is a swap of two entries of A[lo…hi−1]A[lo \ldots hi-1], so the range is permuted and no other entry changes. □\square

Invariant of the main loop. At each test of line L2:

(Q) AA is a permutation of SS; lo≤t<hilo \le t < hi; each item of A[0…lo−1]A[0 \ldots lo-1] is at most each item of A[lo…hi−1]A[lo \ldots hi-1]; and each item of A[lo…hi−1]A[lo \ldots hi-1] is at most each item of A[hi…n−1]A[hi \ldots n-1].

Consequence of (Q). Let BB be the sorted list of the items of A[lo…hi−1]A[lo \ldots hi-1]. Exactly lolo items lie before the block and each is at most each item of BB, and the n−hin - hi items after the block are at least each item of BB. So the sorted order of AA is: a sorted order of A[0…lo−1]A[0 \ldots lo-1], then BB, then a sorted order of A[hi…n−1]A[hi \ldots n-1]. Hence st=B[t−lo]s_t = B[t - lo], the item of rank t−lo+1t - lo + 1 of the block.

  1. Initialization. Line L1 sets lo=0lo = 0, hi=nhi = n and t=k−1t = k - 1, so 0≤t<n0 \le t < n and the outer parts are empty.
  2. Maintenance. Assume (Q) and hi−lo>1hi - lo > 1. Lines L3 and L4 swap two entries of the block. By the lemma, line L5 permutes the block and returns pp with A[lo…p−1]≤A[p]<A[p+1…hi−1]A[lo \ldots p-1] \le A[p] < A[p+1 \ldots hi-1]. If t=pt = p, line L6 returns; this case is treated below. If t<pt < p, line L7 sets hi←phi \leftarrow p. Then lo≤t<plo \le t < p. The items now right of the block are A[p…hi−1]A[p \ldots hi-1] of the old block, which are at least A[p]A[p], which is at least each item of A[lo…p−1]A[lo \ldots p-1], and the old part A[hi…n−1]A[hi \ldots n-1], which (Q) bounds below by each item of the old block. So (Q) holds. If t>pt > p, line L8 sets lo←p+1lo \leftarrow p + 1, and the argument is the mirror one: A[lo…p]A[lo \ldots p] of the old block is at most each item of A[p+1…hi−1]A[p+1 \ldots hi-1].
  3. Termination. In each pass that does not return, the new range is a part of [lo,hi)[lo, hi) that does not hold pp, so hi−lohi - lo drops by at least 1. It starts at nn, so the loop ends after at most n−1n - 1 passes.

Conclusion. The run ends in one of two ways.

  • Line L6 returns A[p]A[p] with p=tp = t. Write lo,hilo, hi for the range of this pass. By the lemma, exactly p−lop - lo items of the block are at most A[p]A[p] and lie before it, and the others are greater. So A[p]A[p] is B[p−lo]=B[t−lo]B[p - lo] = B[t - lo], and by the consequence of (Q) it is sts_t. The items before index tt are at most sts_t by (Q) and the lemma, and the items after it are at least sts_t.
  • The loop stops with hi−lo=1hi - lo = 1 and line L9 returns A[lo]A[lo]. By (Q), lo≤t<hi=lo+1lo \le t < hi = lo + 1, so t=lot = lo and the block is B=[A[t]]B = [A[t]]. By the consequence of (Q), A[t]=B[0]=stA[t] = B[0] = s_t, and (Q) gives the order of the other items.

In both cases the result is st=sk−1s_t = s_{k-1}, and AA is a permutation of SS because each step is a swap. The proof does not use the values of the random indices of line L3, so it holds for every sequence of random choices. ■\blacksquare

Back to Quickselect