The Average Case of Quicksort

· 8 min read · #algorithms #quicksort #sorting #analysis #complexity

This post is the formal companion to the quicksort chapter in my data structures course. In the course notes, we justify quicksort's average-case performance mostly by intuition: a good pivot splits the array into two reasonably balanced pieces, and a sequence of reasonably balanced splits leads to work. Here we do the calculation.

The prerequisites are modest. You should know how quicksort partitions around a pivot, what Big-O notation means, what logarithms are, and the basic idea of probability. You do not need a discrete mathematics course. When we use a tool that may be new, such as expected value, I will spell out what it means.

We will assume the array contains distinct values and that quicksort chooses its pivot uniformly at random from the current slice each time it partitions. If your implementation always chooses the first item as pivot, this is the same kind of analysis you get after randomly shuffling the input first.

Quicksort sorts by partitioning around a pivot: values smaller than the pivot go to one side, values larger than the pivot go to the other, and then quicksort recursively sorts the two sides. Almost all of the algorithmic work we care about here is in comparisons: each partition step compares values against the pivot. So we will count comparisons, averaged over all the random pivot choices.

The final answer will be that random-pivot quicksort makes

comparisons on average, where

For large , that is about

So the familiar average-case running time is not just a slogan. It comes from a precise count of the comparisons quicksort is likely to make.

Averaging a sum one pair at a time

The total number of comparisons is hard to track directly, because it depends on the whole sequence of pivots. Instead of trying to follow the whole run at once, we will look at one pair of values at a time.

Number the values by their place in sorted order. Let be the smallest value, the next smallest, and so on up to , the largest. This numbering is not the array index during the algorithm. It is the value's rank in sorted order.

Take any two values, and , with . During one run of quicksort, this pair is either compared exactly once or never compared at all.

It cannot be compared more than once. Two values are compared only when one of them is the pivot in a partition step. Once a value is chosen as a pivot, it is placed in its final sorted position and is never used in another partition. So the same two values cannot meet again later as pivot and non-pivot.

Now define a small counter for this pair:

The total number of comparisons in the whole run is the sum of these little counters over all pairs:

For example, if and are compared, then contributes to the total. If they are not compared, it contributes .

Now we average. The expected value of a random quantity is its long-run average: if you ran the same randomized algorithm many times and averaged the result, the average would approach the expected value. We write for the expected value of .

The key rule we need is that the expected value of a sum is the sum of the expected values:

In words: to find the average total number of comparisons, we can add up the average contribution of each pair. This rule works even though the different pair counters are not independent.

Each is only or . Its average is therefore just the probability that it is . If a pair is compared with probability , then over many runs it contributes about a fraction of the time and the rest of the time, so its average contribution is .

Therefore

and the expected total number of comparisons is

Now we need that probability.

When are two values compared?

Consider the values through , already in sorted order, so . Take the pair and , and look at the block of values between them, ends included: .

The key fact is this: and are compared if and only if the first pivot chosen from that block is either or .

To see why, follow what happens as pivots are chosen. As long as no pivot comes from the block , all five of those values stay together in one slice. A pivot from outside the block cannot separate them. A pivot of is smaller than all five, so they all go to its right. A pivot of or is larger than all five, so they all go to its left. Either way, the block stays whole.

So the first pivot that falls inside the block is the first one to touch these values, and there are three cases:

  • The first such pivot is . Then is compared against every value in its slice, and is still in that slice, so and are compared.
  • The first such pivot is . By the same reasoning, is compared against .
  • The first such pivot is a middle value, , , or . Say it is . Partitioning around sends and to its left and and to its right. Now and sit in different slices, and nothing ever brings them back together, so they are never compared. Each is compared against , but not against each other.

So and are compared exactly when the first pivot from the block is one of the two ends, not one of the middle values.

The probability

The same reasoning works for any pair. The block between and is

This block has

values: it starts at position , ends at position , and includes both ends.

The values in this block stay together until one of them is chosen as a pivot. Once the first pivot from this block is chosen, there are two possibilities:

  • If the first pivot from the block is or , then the pair is compared.
  • If the first pivot from the block is one of the values strictly between them, then the pair is separated and is never compared.

Because pivots are chosen at random, the first value chosen from this block is equally likely to be any of the values in the block. One way to picture this is to imagine that the values in the block are waiting in a random order to become pivots. The one that appears first in that random order is equally likely to be any of them.

There are possible first pivots from the block. Exactly two of them, and , make the pair get compared. Therefore

The closer two values are in sorted order, the more likely they are compared. Neighbors, where , have a block of two and are compared with probability , always. The smallest and largest values have a block of and are compared with probability only .

Adding it up

Now put that probability into the expected-value sum:

We could add this by looping over all choices of and , but it is cleaner to group pairs by how far apart they are in sorted order.

Let

This is the gap between the two values. If , the values are neighbors in sorted order. If , there is one value between them. In general, a gap of means the block from to has values, so each such pair is compared with probability

How many pairs have gap ? The smaller value can be . After that, the larger value is forced: it must be positions later. So there are pairs with gap .

Therefore

This formula is already the exact average number of comparisons. Now we simplify it.

Set . When , . When , . Also, since ,

So the sum becomes

Split the fraction:

Then

There are two pieces here:

and

Therefore

The sum

is called the $n$th harmonic number and is written . Since

we get

Expanding the expression gives the standard exact formula:

That is the average number of comparisons made by random-pivot quicksort on distinct values.

Why this is O(n log n)

The exact formula contains , so we need to know how fast grows.

The harmonic number

grows like . Intuitively, this sum is a discrete version of the area under the curve . The area under from to is , so the sum grows at the same logarithmic rate.

That means

So from the exact formula,

the main term grows like . The part is only linear, and the factor in front of is proportional to . Therefore the expected number of comparisons is

This is the formal version of the intuition from the course notes: random pivots tend to produce enough reasonably balanced splitting that the average cost is logarithmic levels of partitioning, with about comparison work per level.

The leading constant

The exact formula also tells us the leading constant. Since grows like , the dominant part of

is

Computer science usually writes sorting lower bounds and comparison counts with base-two logarithms. Since

we get

And because

random-pivot quicksort makes about

comparisons on average, ignoring lower-order terms.

No comparison sort can average fewer than about comparisons in the leading term, so quicksort's random-pivot average is only about forty percent above that floor. That is the precise sense in which quicksort is fast on average, and it stands well clear of the behavior that bad pivot choices can produce.