Books arranged neatly on wooden shelves. Photo · Pickawood / Unsplash
Sorting is one of the oldest problems in computing — and one of the most studied. Five canonical answers; one practical winner.

Sorting comes up everywhere — search, deduplication, statistics, rendering order, database results. It is also one of the most beautifully studied problems in algorithms, with a rich folklore. There are dozens of sorting algorithms with names. Most of them are historical curiosities. A few are genuinely useful. The library you'll actually call (std::sort) is itself a hybrid of three of them. We'll meet five today, in roughly the order they were invented.

3
7
1
5
8
2
6
4
Eight bars, scrambled, sorting themselves. The bars are the same — what changes is the order.

The five

Algorithm Best Average Worst In place?
Bubble sort O(n) O(n²) O(n²) yes
Insertion sort O(n) O(n²) O(n²) yes
Selection sort O(n²) O(n²) O(n²) yes
Mergesort O(n log n) O(n log n) O(n log n) no (needs O(n) extra)
Quicksort O(n log n) O(n log n) O(n²)* yes

*Quicksort's O(n²) worst case requires pathologically bad pivots; modern implementations use random or median-of-three pivots to make it astronomically rare.

The two that matter — quicksort and mergesort

Quicksort picks a pivot, partitions the data into "less than pivot" and "greater than pivot", recurses on each. Beautifully simple, fast in practice, in-place. Its worst case is bad but practically unreachable with reasonable pivot selection.

// quicksort sketch
void quicksort(std::vector<int>& v, int lo, int hi) {
    if (lo >= hi) return;
    int p = partition(v, lo, hi);
    quicksort(v, lo, p - 1);
    quicksort(v, p + 1, hi);
}

Mergesort splits the array in half, recursively sorts each half, merges them back together. Always O(n log n) — no bad cases. Stable (preserves relative order of equal elements). Needs O(n) auxiliary memory for the merge step. The default for sorting linked lists, external sorting (data on disk), and stable in-memory sorts.

What std::sort actually does

Modern std::sort implementations use a hybrid called introsort: start with quicksort, switch to heapsort if recursion goes too deep (avoiding the O(n²) worst case), and use insertion sort for small partitions where it's fastest. Three algorithms in a trench coat, doing the right thing at every scale.

The result: O(n log n) guaranteed worst case, very fast in practice, in-place. Usable for almost any sorting need without thought. Don't write your own sort. The standard library spent decades getting this right.

When you'd choose differently

Most of the time, none of these come up. Use std::sort.

Why this matters for AI

Top-K selection — picking the K best logits from a probability distribution at each token — is technically O(n) (you don't need to fully sort), but partial sorts and quickselect are still in the inner loop of every language model's sampling code. std::partial_sort handles it.

Sorting also appears in dataset preprocessing (deduplicating training data is a sort + scan), in evaluation (ranking model outputs), and in indexing (vector databases use sort+search throughout).

Use the library. Forty years of algorithmic engineering is already in there.

Try it yourself

What's next

Week 34 is Linked Lists — the data structure that pointers were invented for, and the lessons learned from forty years of "should I use one of these?".

Photo credit

Photo free under the Unsplash license. Books · Pickawood.

Quick check

1. What is bubble sort's worst-case complexity?
  1. O(N)
  2. O(N log N)
  3. O(N²)
Reveal Answer

Answer: C. Bubble sort is O(N²). It exists in textbooks for clarity, not because anyone uses it in practice.

2. What is quicksort's average-case complexity?
  1. O(N²)
  2. O(N log N)
  3. O(N)
Reveal Answer

Answer: B. O(N log N) average; O(N²) worst case (already-sorted input with naive pivot). Production sorts mitigate the worst case.

3. Why is std::sort typically faster than rolling your own quicksort?
  1. It's introsort: quicksort + heapsort fallback + insertion sort for small ranges, with decades of tuning
  2. It runs on a separate thread
  3. It calls the OS
Reveal Answer

Answer: A. Years of compiler-team tuning. Almost always faster than what you'll write.