Definition · QuicksortTo quicksort a[lo..hi]: if the range holds fewer than two elements, stop. Otherwise partition it around a pivot chosen by a pivot rule, then quicksort the part left of the pivot and the part right of it. The pivot itself is never looked at again.
There is no merge step and no extra array. All the work is in the partitions, and by “Partitioning” each partition of a range of length m costs m − 1 comparisons. So the comparison count of a whole run is determined by the multiset of range lengths the recursion visits, and that is determined by the splits.
T(n) = T(k) + T(n − k − 1) + (n − 1) T(0) = T(1) = 0
k = n/2 each time: T(n) ≈ 2T(n/2) + n ⟹ T(n) = Θ(n log n)
k = 0 each time: T(n) = T(n − 1) + (n − 1) = n(n − 1)/2
Those two lines are the same recurrence with two values of k, and the gap between their solutions is the whole subject of the lesson. Discrete Mathematics' “Divide and Conquer” solves the first by the master theorem and “Recursion Trees and Amortised Analysis” gives the tree argument the second one is a degenerate case of. Neither tells you which k you get; the pivot rule and the input do.
Example · A first-element pivot on sorted inputThe pivot is the smallest key, so k = 0: the left side is empty and the right side has n − 1 elements. Then the same thing happens again. At 32 keys the lab measures 496 comparisons and a recursion depth of 31, against n(n − 1)/2 = 496 — the recurrence's worst case, met exactly. Already-sorted input is the commonest input there is, which is what makes this rule not merely theoretically bad.
Building the array that defeats a rule
The lab's killer input is constructed, not recalled. It runs the chosen rule on n items whose values are not yet decided, and whenever the rule compares two undecided items it freezes one of them at the next smallest value available — choosing which one so that the pivot keeps turning out to be near the smallest thing left. Every answer it gives is consistent with the values it finally hands back, so what comes out is a real permutation of 1 … n and not a trick.
That is the same manoeuvre as the lower-bound arguments at the end of this course: answer the algorithm's questions so as to keep the bad case alive. Here it is aimed at one rule rather than at every algorithm, which is why it produces an input rather than a bound.
Example · Median of three, against its own killerAt 32 keys the array the lab builds is 1 18 4 26 6 20 8 30 10 22 12 28 14 24 16 2 3 5 7 … and the adversary answered 316 of the rule's comparisons while building it. The ordinary quicksort then spends 304 comparisons on it and recurses 16 deep, where a balanced split would be about 6 deep and n log₂ n is 160. Halve the length to 16 and the count is 88; doubling n multiplied the work by 3.45.
A count that roughly quadruples when n doubles is behaving quadratically and a count that roughly doubles is not, and the lab prints the ratio at two sizes rather than reading a verdict off a curve. Two sizes are two measurements. The n log₂ n and n(n − 1)/2 lines on the plot are drawings sampled at double precision; every verdict on the panel compares integers.
Two further readings from the same panel are worth having. First, rule-specificity: the array built against median of three costs a first-element pivot 143 comparisons, and the array built against a first-element pivot costs a middle-element pivot 103. Each rule has its own bad input, which is exactly what it means for the flaw to be in the rule rather than in quicksort. Second, the array of 24 equal keys: every rule pays 276 there, n(n − 1)/2 again, because Lomuto sends every key equal to the pivot to one side.
So what is quicksort's cost? On the input in front of you, whatever the lab measured. As a claim about all inputs under a deterministic rule, Θ(n²), because the killer exists. “Randomised Quicksort” changes the question by changing where the randomness lives, and gets an answer that is exact and holds on every input at once.