Algorithm Search⌘K

Proofs: Quicksort

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

Quicksort sorts the array and keeps its items full

This proof shows: correctness.

We write SS for the multiset of the items of A[lo…hi−1]A[lo \ldots hi-1] at the start of a call. Each step of the algorithm changes the array only by a swap, so the multiset of the items of the full array never changes.

Lemma (PARTITION). Assume that hi−lo≥2hi - lo \ge 2. Then PARTITION(A,lo,hi)(A, lo, hi) returns an index pp with lo≤p≤hi−1lo \le p \le hi - 1. After the call, A[lo…hi−1]A[lo \ldots hi-1] holds SS, A[p]=xA[p] = x (the old A[hi−1]A[hi-1]), each item of A[lo…p−1]A[lo \ldots p-1] is at most xx, and each item of A[p+1…hi−1]A[p+1 \ldots hi-1] is larger than xx. No item outside A[lo…hi−1]A[lo \ldots hi-1] changes.

Proof. Line L5 sets x=A[hi−1]x = A[hi-1], and line L6 sets i=loi = lo. We use this invariant for the loop of line L7, before the step for jj:

(P) lo≤i≤j≤hi−1lo \le i \le j \le hi - 1. 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 larger than xx. A[hi−1]=xA[hi - 1] = x.

Start. For j=loj = lo, both parts are empty and i=j=loi = j = lo. Nothing has moved, so A[hi−1]=xA[hi-1] = x.

Step. Assume (P) before the step for j≤hi−2j \le hi - 2. If A[j]>xA[j] > x, the step changes nothing, and A[i…j]A[i \ldots j] is a part of items larger than xx. So (P) holds for j+1j + 1. If A[j]≤xA[j] \le x, line L9 swaps A[i]A[i] and A[j]A[j]. If i<ji < j, then A[i]A[i] was in the part of larger items, so after the swap A[i]≤xA[i] \le x and A[j]>xA[j] > x. If i=ji = j, the swap changes nothing. In both cases, after line L10, each item of A[lo…i−1]A[lo \ldots i-1] is at most xx, and each item of A[i…j]A[i \ldots j] is larger than xx. So (P) holds for j+1j + 1. The swap uses indices i≤j≤hi−2i \le j \le hi - 2, so A[hi−1]A[hi - 1] does not change.

End. The loop runs once for each jj from lolo to hi−2hi - 2, so it stops after hi−1−lohi - 1 - lo steps with j=hi−1j = hi - 1. By (P), A[lo…i−1]≤xA[lo \ldots i-1] \le x, A[i…hi−2]>xA[i \ldots hi-2] > x and A[hi−1]=xA[hi-1] = x. Line L11 swaps A[i]A[i] and A[hi−1]A[hi-1]. If i<hi−1i < hi - 1, the item at ii was larger than xx, and it goes to index hi−1hi - 1, which is in the right part. If i=hi−1i = hi - 1, the swap changes nothing. So with p=ip = i, the claim holds. All changes were swaps inside the range, so the range holds SS and nothing outside it changed. □\square

Theorem. After QUICKSORT(A,lo,hi)(A, lo, hi), the part A[lo…hi−1]A[lo \ldots hi-1] is sorted and holds SS. No other item of AA changes.

Proof. By strong induction on k=hi−lok = hi - lo.

Base. If k≤1k \le 1, line L1 returns. A part with at most one item is sorted, and nothing changed.

Step. Let k≥2k \ge 2, and assume the claim for all ranges of length less than kk. By the lemma, line L2 gives pp with lo≤p≤hi−1lo \le p \le hi - 1, a left part A[lo…p−1]A[lo \ldots p-1] of items at most xx, and a right part A[p+1…hi−1]A[p+1 \ldots hi-1] of items larger than xx. The ranges of lines L3 and L4 have lengths p−lop - lo and hi−p−1hi - p - 1. Both are at most k−1k - 1, because the index pp is in neither range. By the induction hypothesis, line L3 sorts the left part and changes nothing else, so it does not change A[p]A[p] or the right part. It permutes the left part, so all its items are still at most xx. In the same way, line L4 sorts the right part and keeps its items larger than xx.

Now A[lo…p−1]A[lo \ldots p-1] is sorted with each item at most xx, A[p]=xA[p] = x, and A[p+1…hi−1]A[p+1 \ldots hi-1] is sorted with each item larger than xx. So A[lo…hi−1]A[lo \ldots hi-1] is sorted. Each step was a swap inside the range, so it holds SS. ■\blacksquare

Termination. Each call on a range of length k≥2k \ge 2 makes two calls on ranges of length at most k−1k - 1. So the depth of the recursion is at most n−1n - 1, and the algorithm stops.

Back to Quicksort