StudyHub

Algorithms · Sorting

Quick sort

Take [5, 2, 6, 1, 4] and pick the last number, 4, as the pivot. Move everything ≤ 4 to its left and everything bigger to its right: [2, 1, 4, 5, 6]. The 4 is now in its final place. Do the same to each side, and you have quick sort.

Why it matters: every partition puts one pivot in its final place for good. With reasonably balanced pivots, the whole sort takes $O(n \log n)$ in place, and in practice it's usually the fastest comparison sort.

Prerequisites

  • Recursion: a function that calls itself on a smaller input.
  • Swapping two array elements.
  • Big-O notation (see Complexity analysis), and ideally Merge sort.

The analogy

Everyday analogy

A coach picks one player as the reference height. Everyone shorter goes to the left wall, everyone taller to the right wall, and the reference player stands in the middle.

That middle spot is already correct. Each wall group then picks its own reference and splits again, until every group is one person. That's quick sort, with the pivot as the reference player.

How it works

Start with one partition by hand

Take [5, 2, 6, 1, 4], pivot = 4. Walk left to right; every number ≤ 4 gets swapped into a growing "left zone" at the front. Guess first, then open "Check".

Example: one partition, predict before you read

Step 1. Look at 5. Predict: does it join the left zone?

Check

No, 5 > 4. Skip it. The left zone is still empty.

Step 2. Cover the next line and guess: what happens to 2?

Check

2 ≤ 4, so it swaps to the front of the zone: [2, 5, 6, 1, 4]. 6 is next and is skipped.

Step 3. Now 1. Then the scan ends. Predict: where does the pivot go?

Check

1 ≤ 4 joins the zone: [2, 1, 6, 5, 4]. The pivot swaps into the slot right after the zone: [2, 1, 4, 5, 6].

Lomuto partition of 5 2 6 1 4 with pivot 4: 2 and 1 move into the left zone, then the pivot lands at index 2, giving 2 1 4 5 6.
What you just did by hand: blue is the left zone (≤ pivot). The pivot ends in green, in its final place.

Quick recall: Before reading further, cover the text above and say out loud: what does one partition guarantee?

Check your answer

The pivot is in its final position, with everything ≤ it on the left and everything bigger on the right.

Finish the run

Now sort each side of [2, 1 | 4 | 5, 6] the same way.

  • Left side [2, 1], pivot 1. Predict the result.
    Check

    2 > 1 is skipped, then 1 swaps to the front: [1, 2].

  • Right side [5, 6], pivot 6. Predict: does anything move?
    Check

    No. 5 ≤ 6 stays in the zone and 6 stays last. Final array: [1, 2, 4, 5, 6].

Quick recall: Without scrolling up, say out loud: why is the 4 never touched again after the first partition?

Check your answer

It's already in its final place: everything left of it is smaller or equal and everything right is bigger. The recursion only works on the two sides.

The rule in plain words

Pick the last number as the pivot. Walk through the rest; swap every number ≤ pivot into the left zone. Finally, swap the pivot right after the zone.

The pivot is now final. Repeat on the part left of it and the part right of it. A part of size 0 or 1 is already sorted.

Quick recall: Before you read the code, write the three steps of quick sort in your own words. Then check below.

Check your answer

1. Choose a pivot. 2. Partition: smaller-or-equal numbers to the left, bigger to the right, pivot in between. 3. Sort the left and right parts the same way.

The code

Read this code after you have done one partition by hand. It will make more sense.

static void swap(int *x, int *y) { int t = *x; *x = *y; *y = t; }
int partition(int a[], int lo, int hi) {     /* Lomuto, pivot = a[hi] */
    int pivot = a[hi], i = lo - 1;           /* a[lo..i] is the left zone */
    for (int j = lo; j < hi; j++)
        if (a[j] <= pivot) swap(&a[++i], &a[j]);
    swap(&a[i + 1], &a[hi]);                 /* pivot after the zone */
    return i + 1;
}

void quick_sort(int a[], int lo, int hi) {   /* sorts a[lo..hi] */
    if (lo >= hi) return;
    int p = partition(a, lo, hi);
    quick_sort(a, lo, p - 1);                /* a[p] is final: skip it */
    quick_sort(a, p + 1, hi);
}

Definition

What you just did, in one line: quick sort picks a pivot, partitions the array so the pivot lands in its final place with smaller-or-equal elements on its left, then sorts both sides recursively.

graph TD
  A["5 2 6 1 4 (pivot 4)"] -->|partition| B["2 1 | 4 | 5 6"]
  B --> C["2 1 (pivot 1)"]
  B --> D["5 6 (pivot 6)"]
  C -->|partition| E["1 | 2"]
  D -->|partition| F["5 | 6"]
  E & F --> G["1 2 4 5 6"]
          
Before you scroll past this diagram, cover the bottom half and guess: after partitioning [2, 1] around 1, what is the result? (Answer: [1, 2].)
Bar chart of 7 2 9 4 1 8 3 5 before and after partitioning around pivot 5: afterwards 2 4 1 3 are left of the pivot and 8 7 9 are right of it.
The same partition on 8 numbers, pivot 5: one linear pass puts the pivot at index 4.

How fast is it?

Quick recall: Before looking at the formula, predict: what if the pivot is always the largest number in its part, as on a sorted array?

Check your answer

Each partition only removes the pivot. The left part is everything else and the right part is empty, so the parts shrink by one each time.

With balanced pivots there are about $\log_2 n$ levels of $n$ work each, so $O(n \log n)$. With the worst pivots, the parts shrink by one:

$$(n-1) + (n-2) + \dots + 1 \;=\; \frac{n(n-1)}{2} \;=\; O(n^2).$$

Random pivots, or the median of the first, middle and last elements, make the bad case very unlikely.

BestAverageWorstExtra spaceStableIn-place
$O(n \log n)$$O(n \log n)$$O(n^2)$$O(\log n)$ stackNoYes

Warning

"In-place" doesn't mean $O(1)$ memory here. The recursion stack is $O(\log n)$ on average, and can grow to $O(n)$ with bad pivots unless you recurse on the smaller side first.

Exam tip

A classic question: quick sort on an already sorted array with the first or last element as pivot. Every split is as unbalanced as possible, so the answer is $O(n^2)$.

Common mistakes

  • Forgetting the final swap that puts the pivot after the left zone.
  • Recursing on [lo, p] instead of [lo, p - 1], which can recurse forever on the same range.
  • Using a fixed first or last pivot on data that may already be sorted.

Try it yourself

Exercise 1 (warm-up)

Partition [3, 7, 1, 5] with pivot 5 (the last element). What is the array afterwards, and where is the pivot?

Hint

3 and 1 join the left zone; 7 doesn't.

Answer

[3, 1, 5, 7], with the pivot 5 at index 2.

Exercise 2 (a bit harder)

How many comparisons does this quick sort make on [1, 2, 3, 4, 5]? Write your answer down before you open it.

Show answer

4 + 3 + 2 + 1 = 10, which is $\frac{n(n-1)}{2}$. Every pivot is the largest, so each right part is empty and the left part shrinks by one.

Did you know?

Did you know?

Tony Hoare invented quick sort in 1959, as a visiting student in Moscow, while working on sorting words for machine translation.

Today variants of it sort arrays of numbers in C++'s std::sort and Java's Arrays.sort for primitives. Learn this one and you understand what most standard libraries do.

Key points to remember

If you remember one thing from each line, remember the because.

  • After a partition the pivot is final, because everything left of it is ≤ it and everything right of it is bigger.
  • The average case is $O(n \log n)$ because reasonably balanced splits give about $\log_2 n$ levels of $n$ work.
  • The worst case is $O(n^2)$ because a pivot that is always the min or max shrinks the problem by only one.
  • It is not stable because partition swaps can jump an element over an equal one.
  • Its extra space is $O(\log n)$ because the only memory it uses is the recursion stack.

Self-check questions

Answer these in order, out loud. They follow the page, so your answers become the script of your video.

  1. How is quick sort like a coach splitting players by height?
    Sample answer

    The coach picks a reference player, sends shorter players left and taller players right, and the reference player's spot is now correct. Each group then repeats the process.

  2. Walk through the first partition of [5, 2, 6, 1, 4] with pivot 4.
    Sample answer

    5 is skipped, 2 joins the left zone, 6 is skipped, 1 joins the zone, then 4 swaps in after the zone: [2, 1, 4, 5, 6].

  3. Why does the code recurse on p - 1 and p + 1, not on p?

    A good answer says what is special about index p.

    Sample answer

    The pivot at p is already in its final place, so there's nothing left to do there. Including it could also make a range never shrink, giving infinite recursion.

  4. So what is quick sort, in one sentence?
    Sample answer

    It partitions the array around a pivot so the pivot lands in its final place, then sorts the two sides the same way.

  5. Why does a sorted array make this quick sort $O(n^2)$?

    A good answer says how big the two sides are.

    Sample answer

    With the last element as pivot, the pivot is always the maximum, so one side is empty and the other has everything else. The sizes go $n-1$, $n-2$, …, adding up to $\frac{n(n-1)}{2}$.

  6. What are its best, average and worst cases, and its extra space?
    Sample answer

    $O(n \log n)$ in the best and average case, $O(n^2)$ in the worst case. Extra space is the recursion stack, $O(\log n)$ on average.

  7. How do you make the bad case unlikely?
    Sample answer

    Choose a random pivot, or the median of the first, middle and last elements. Then no particular input order, like sorted, can trigger the worst case on purpose.

  8. Quick sort is usually fastest. Why would anyone choose merge sort or heap sort instead?
    Sample answer

    Merge sort is stable and guarantees $O(n \log n)$. Heap sort guarantees $O(n \log n)$ with $O(1)$ extra memory. Quick sort offers neither guarantee.

  9. If you had to teach this to a friend in 2 minutes, what would you leave out, and why?
    Sample answer

    I'd leave out the Lomuto index details and the stack-space discussion. I'd keep the coach analogy, one partition on a small array, "the pivot is final", and "fast on average, $O(n^2)$ with bad pivots".

External resources

Videos

Review schedule

Come back after 3, 7, 14, then 30 days. Spacing beats cramming.

Feynman prompt

Explain this topic in your own words, as if teaching a beginner.

Explain it in your own words first, then record a 2–3 minute video on your phone.

25:00

Ready to record? Open your phone camera now and record a 2–3 minute explanation.

My notes

Saved only in this browser (localStorage). Nothing is uploaded.