thecodex.expert · The Codex Family of Knowledge
Algorithms

Sorting Algorithms

The Θ(n log n) lower bound, stability, and how the best sort algorithms work.

Algorithms Requires: CS Concepts Big O included Last verified:
Canonical Definition

Sorting algorithms arrange elements in order. Comparison sorts have an O(n log n) lower bound proven by decision tree argument. Non-comparison sorts can achieve O(n) by exploiting key structure.

One sentence

Sorting is the task of arranging a list in order — and choosing the right algorithm determines whether the task takes milliseconds or hours.

Why sorting matters

Sorting is one of the most studied problems in computer science. A sorted list enables binary search (O(log n) vs. O(n)). Sorting is a prerequisite for many other algorithms. And sorting benchmarks reveal fundamental limits: the best possible comparison-based sort is Θ(n log n) — provably. No comparison sort can do better.

AlgorithmBestAverageWorstSpaceStable?
Merge SortΘ(n log n)Θ(n log n)Θ(n log n)O(n)Yes
QuicksortΘ(n log n)Θ(n log n)Θ(n²)O(log n)No*
HeapsortΘ(n log n)Θ(n log n)Θ(n log n)O(1)No
Insertion SortΘ(n)Θ(n²)Θ(n²)O(1)Yes
Bubble SortΘ(n)Θ(n²)Θ(n²)O(1)Yes
Counting SortΘ(n+k)Θ(n+k)Θ(n+k)O(n+k)Yes

* Stable quicksort implementations exist but the standard in-place version is not stable. k = range of values for counting sort.

Stability

A sort is stable if equal elements remain in their original relative order. Sorting records by last name: if two records have the same last name, a stable sort preserves their original order. Merge sort is always stable. Python's built-in sort (sorted() / list.sort()) uses Timsort — stable. Java's Arrays.sort() for objects uses merge sort — stable. Java's Arrays.sort() for primitives uses dual-pivot quicksort — not stable.

The O(n log n) lower bound for comparison sorts

Any algorithm that sorts by comparing elements must make at least Ω(n log n) comparisons in the worst case. Proof by decision tree: a sort algorithm that works by comparisons can be modelled as a binary decision tree where each internal node is a comparison and each leaf is a permutation. There are n! possible orderings — so the tree has at least n! leaves. A binary tree of depth d has at most 2d leaves. Therefore depth ≥ log₂(n!) = Θ(n log n) by Stirling's approximation. Any comparison-based sort must have worst case Ω(n log n). This bound is proven in CLRS §8.1.

Non-comparison sorts

Counting sort, radix sort, and bucket sort are not comparison sorts — they exploit structure in the key values themselves. Counting sort runs in O(n+k) time where k is the range of values. If k = O(n), this is O(n) — beating the comparison sort lower bound. The lower bound applies only to comparison-based algorithms; these algorithms don't compare elements, so the bound doesn't apply.

Python's Timsort

Python's built-in sort uses Timsort (Tim Peters, 2002). Timsort is a hybrid algorithm: it detects natural runs (already-sorted subsequences) in the data, extends short runs using insertion sort (which is fast for small n due to low constant), then merges runs using a merge sort strategy. Worst case: Θ(n log n). Best case: Θ(n) on already-sorted data. Stable. Space: O(n). Specified in the Python documentation and CPython source (Objects/listobject.c).

Commonly confused
A faster best case doesn't make an algorithm better overall. Insertion sort is O(n) on already-sorted data, but O(n²) on average. For random data, merge sort or quicksort will be much faster. Best-case analysis is often irrelevant to real workloads.
In-place does not mean O(1) space. Quicksort is "in-place" (sorts within the array) but uses O(log n) stack space for the recursive calls. Truly O(1) extra space is heapsort.
Stable does not mean correct and unstable does not mean incorrect. Both stable and unstable sorts produce a correctly sorted output. Stability only matters when you need the relative order of equal elements to be preserved — for example, a multi-key sort.
How this connects
Enables
Confused with

The comparison sort lower bound: formal proof

Formally (CLRS 4th ed., §8.1): model any comparison sort as a decision tree T. Each internal node corresponds to a comparison a​𝑖 ≤ a​𝑗. Each leaf corresponds to one of the n! possible permutations. Since every leaf must be reachable (any permutation is a valid input), T must have at least n! leaves. A binary tree of height h has at most 2h leaves, so h ≥ log₂(n!). By Stirling's approximation: log₂(n!) = Θ(n log n). Therefore any comparison sort requires Ω(n log n) comparisons in the worst case.

Integer sorting and the RAM model

The Ω(n log n) lower bound assumes the RAM model with unbounded word size. If keys are w-bit integers and n ≤ 2w (realistic constraint), radix sort runs in O(wn) = O(n log n) time but with a much smaller constant than merge sort in practice. On a word-RAM model where w = Θ(log n), radix sort matches the comparison sort lower bound — but beats it in practice because the constant is smaller.

Specification reference

Cormen, T. H. et al. (2022). CLRS (4th ed.), §8.1 — Lower bounds for sorting. Peters, T. (2002). Timsort algorithm description — CPython source Objects/listobject.c. Python documentation: docs.python.org/3/library/functions.html#sorted.

Sources

1
Cormen, T. H. et al. (2022). Introduction to Algorithms (4th ed.), §8.1 — Lower bounds for sorting; §8.2 — Counting sort; §8.3 — Radix sort. MIT Press.
2
Peters, T. (2002). Timsort. CPython source Objects/listobject.c; description at svn.python.org/projects/python/trunk/Objects/listsort.txt.
3
Knuth, D. E. (1998). The Art of Computer Programming, Vol. 3: Sorting and Searching (2nd ed.). Addison-Wesley.
4
Sedgewick, R. & Wayne, K. (2011). Algorithms (4th ed.), Ch. 2 — Sorting. Addison-Wesley.
Source confidence: High Last verified: Primary source: Cormen et al. CLRS (4th ed.) §8.1