Intuition
Picture sorting a stack of exam papers. Pick one score, make a pile of everything lower and a pile of everything higher, then repeat on each pile until every pile has one paper.
Quicksort does exactly that, inside the array itself. One pass puts the pivot where it will sit in the finished list. Everything left of it is smaller, everything right of it is not, and the two sides never need to meet again.
How it works
The figure uses the Lomuto partition, the easiest version to follow:
- Take the last value in the range as the pivot.
- Walk
jfrom the start of the range to just before the pivot. Keep a second index,i, marking where the next small value should go. - Each time
a[j]is smaller than the pivot, swap it into positioniand moveione step right. - When the walk ends, every value before
iis smaller than the pivot. Swap the pivot into positioni: that is its final place. - Sort the part left of
iand the part right ofithe same way. A range of zero or one values is already sorted.
Step through the figure and watch the code: the comparison line lights up blue, and the two swap lines light up orange.
Complexity
| Case | Time | When it happens |
|---|---|---|
| Best | O(n log n) | Every pivot splits its range roughly in half. |
| Average | O(n log n) | Random input. Uneven splits even out over many partitions. |
| Worst | O(n²) | The pivot is always the smallest or largest value, such as already sorted input with a last-element pivot. |
Each level of recursion does about n comparisons in total. Balanced splits give about log₂ n levels, and lopsided ones give up to n levels. The extra space is the recursion stack: about log n deep on average, but n deep in the worst case unless the code always recurses into the smaller side first.
Common mistakes
- No base case. The recursion must stop on ranges of size 0 or 1.
- Recursing on a range that still includes the pivot. The range never shrinks, so the sort never finishes.
- Assuming it is stable. Equal values can swap order. Use a merge sort when that matters.
- A fixed pivot on sorted data. Always taking the last value turns already-sorted input into the worst case.
When to use it
Use quicksort when you sort arrays in memory and want speed with little extra space. It reads memory in order, which caches handle well, and on random data it is usually faster than merge sort in practice.
Avoid a hand-written quicksort when the order of equal values matters, when an attacker could choose the input to force the worst case, or when you are sorting a linked list. In real code, call your language’s built-in sort, which already guards against these cases.
In other languages
| Language | Built-in sort | Uses quicksort? |
|---|---|---|
| Python | list.sort(), sorted() |
No: a stable merge sort (Timsort family) |
| JavaScript | Array.prototype.sort() |
No: stable since ES2019; engines use merge-based sorts |
| Java | Arrays.sort(int[]) |
Yes: dual-pivot quicksort for primitives. Objects use a stable merge sort |
| C | qsort() |
Not guaranteed: the standard doesn’t name an algorithm |
| Go | slices.Sort() |
Yes: pattern-defeating quicksort (pdqsort) |
| Rust | sort_unstable() |
Yes: a quicksort variant. sort() is a stable merge sort |
Check yourself
Check yourself
Partition [7, 2, 9, 4, 5] using the last value, 5, as the pivot. Where does 5 end up?
Two values, 2 and 4, are smaller than 5, so the pivot lands at index 2. The array becomes [2, 4, 5, 7, 9].
Check yourself
This quicksort sorts [1, 2, 3, 4, 5], which is already in order. How many comparisons does it make?
The pivot is always the largest value, so each range shrinks by just one: 4 + 3 + 2 + 1 = 10 comparisons, which is n(n−1)/2.
Sources
- C. A. R. Hoare, “Quicksort,” The Computer Journal 5, no. 1 (1962): 10–16.
- Cormen, Leiserson, Rivest and Stein, Introduction to Algorithms, chapter 7, “Quicksort.”
Finished this page?
Your list