ConceptDifficulty: Intermediate4 min

Quicksort: Partition Around a Pivot, Then Sort Each Side

Smaller items go left of the pivot and larger ones right, which leaves the pivot in its final place.

Quicksort is a sorting algorithm that picks one item, the pivot, moves every smaller item to its left and every other item to its right, and then sorts the two sides by calling itself on each. This card covers that rearranging step, called a partition, the order the calls run in on seven numbers, and the signs that quicksort is the sort to reach for.

After a partition the pivot is in its final place. Every item to its left is smaller and every item to its right is at least as large, so no later step will move it. The two sides are still unsorted inside, but no item ever has to cross from one side to the other, so each side can be sorted on its own, and once both are sorted the whole array is. There is no merge step: merge sort does its work combining the halves, and quicksort does all of its work in the partition.

Sorting 7 2 8 1 5 3 6 shows the order of the calls. This version always takes the last item of the range as its pivot. The trace has one frame per partition: low and high mark the range being partitioned, and pivot marks the index where the pivot landed.

Quicksort on 7 2 8 1 5 3 6, one frame per partition

Input7 2 8 1 5 3 6

  1. Start

    1. 7low
    2. 2
    3. 8
    4. 1
    5. 5
    6. 3
    7. 6high

    The whole array is one range, from low, index 0, to high, index 6.

  2. Pivot 6

    1. 2low
    2. 1
    3. 5
    4. 3
    5. 6pivot
    6. 7
    7. 8high

    Partitioning around 6, the last item, puts it at index 4, its final place. The sides 2 1 5 3 and 7 8 are still unsorted.

  3. Pivot 3

    1. 2low
    2. 1
    3. 3pivot
    4. 5high
    5. 6
    6. 7
    7. 8

    The left range, indices 0 to 3, is partitioned around its last item, 3, which lands at index 2.

  4. Pivot 1

    1. 1lowpivot
    2. 2high
    3. 3
    4. 5
    5. 6
    6. 7
    7. 8

    The range 2 1 is partitioned around 1, which lands at index 0. Every range left on this side holds one item.

  5. Pivot 8

    1. 1
    2. 2
    3. 3
    4. 5
    5. 6
    6. 7low
    7. 8highpivot

    The right range, 7 8, is partitioned around 8, which stays at index 6, and the array is sorted.

Result1 2 3 5 6 7 8

Four partitions sorted seven items. A range of one item, or of none, is never partitioned: it is the base case, already sorted. The pivots in this run split their ranges reasonably well, 6 landing at index 4 of 0 to 6. Had every pivot been the largest or smallest item of its range, each partition would have set aside a single item, and the calls would have gone n − 1 deep instead of about log₂ n.

Lining a class up by height works the same way. Pick Sam, send everyone shorter than Sam to the left and everyone else to the right, and Sam is standing exactly where Sam belongs; then do the same inside each group. The analogy breaks on the choice. A teacher picks someone of middling height by eye, while quicksort picks its pivot without looking at the others, and an unlucky pick leaves one group empty and the other holding everyone.

Cutting 7 2 8 1 5 3 6 at its middle position, into 7 2 8 and 1 5 3 6, and sorting each part is not quicksort. The parts overlap in value, so sorting them gives 2 7 8 and 1 3 5 6, which still have to be merged: that is merge sort. A partition divides by value, and that is what lets quicksort skip the merge.

Quicksort on an array of n items
Best case
O(n log n)
every pivot lands near the middle of its range
Average case
O(n log n)
pivots chosen at random, or inputs in random order
Worst case
O(n²)
every pivot is the largest or smallest item of its range
Extra space
O(log n) expected
the recursion stack; O(n) in the worst case unless the smaller side is sorted first
Stable
No
a swap can carry an item past an equal one
In place
Yes
no second array, only the stack

Three signs point to quicksort. The items are numbers or other keys where equal items cannot be told apart, so stability does not matter. Memory is tight, and merge sort's O(n) buffer is more than you can spare. And speed on typical inputs matters more than a guarantee on every input: each partition compares every item with one fixed value and walks the array in order, which modern hardware does quickly, and a random pivot makes the slow case a matter of extraordinary bad luck rather than of any particular input. Where the worst case has to be ruled out entirely, the libraries that use quicksort guard it, as Java's Arrays.sort on primitive arrays does by switching to heapsort when the recursion runs too deep.

← → move through sections and cards