Lower bound and linear-time sorts
Why no comparison sort can beat n log n (the decision tree argument with n! leaves), how counting sort, radix sort and bucket sort sort in linear time by using the values of the keys instead of comparisons, their costs Theta(n + k), Theta(d(n + k)) and expected Theta(n), why stability makes radix sort correct, and when each applies.
Why it exists
Merge sort and heap sort take . Can any algorithm do better? The answer has two halves, and both are exam material. No, if the only thing the algorithm may do with the keys is compare two of them: comparisons are unavoidable. Yes, if the keys are small integers (or digits, or uniformly spread numbers): then counting sort, radix sort and bucket sort run in linear time, by looking inside the keys instead of comparing them.
Prerequisites
You need the comparison sorts (merge sort, quicksort) and the idea of stability (stability and comparison of sorts).
Precise definitions
Comparison sort model. The algorithm learns about the keys only through questions "is ?" with a yes/no answer. Every comparison sort in the syllabus (bubble, selection, insertion, merge, quick, heap) is of this kind.
Decision tree. For a fixed , the behaviour of a comparison sort can be drawn as a binary tree: each internal node is a comparison of two positions, its two children correspond to the two answers, and each leaf is a final answer, namely a permutation that sorts the input. The number of comparisons on an input is the depth of the leaf reached; the worst case is the height of the tree.
Counting sort. For integer keys in : count how often each key occurs, turn the counts into end positions by prefix sums, and place the keys into the output by scanning the input from the last key to the first.
Radix sort (LSD). For keys of digits in base : for the digit positions from the least significant to the most significant, sort the whole array by that digit with a stable sort (counting sort).
Bucket sort. For keys spread uniformly over : distribute the keys into buckets (key to bucket ), sort each bucket (insertion sort), and concatenate.
How it works
The lower bound
Claim. Every comparison sort needs at least comparisons in the worst case.
Proof. If the keys are distinct there are possible orders, and a correct algorithm must reach a different leaf for each (two different orders need different permutations as output). So the decision tree has at least leaves. A binary tree of height has at most leaves, so , i.e. . By Stirling's approximation , hence The same argument applies to the average number of comparisons: the average depth of leaves of a binary tree is at least . So no comparison sort has average or worst case.
Consequences: merge sort and heap sort are asymptotically optimal comparison sorts; quicksort is optimal on average. The table shows how the bound compares with two real algorithms for small .
How few comparisons can sorting need?: start
Computed by a script, not typed| n | n! (possible orders) | ceil(log2 n!) (lower bound) | binary insertion sort (worst) | merge sort (worst) | n log2 n - 1.4427 n |
|---|---|---|---|---|---|
| 1 | |||||
| 2 | |||||
| 3 | |||||
| 4 | |||||
| 5 | |||||
| 6 | |||||
| 7 | |||||
| 8 | |||||
| 9 | |||||
| 10 | |||||
| 11 | |||||
| 12 |
Step 0
A comparison sort must distinguish all n! orders; each comparison has two outcomes, so it needs at least ceil(log2 n!) comparisons in the worst case. Two real algorithms are shown above that bound.
For the bound is exactly attained: an exhaustive search over all strategies gives the minimum worst-case numbers of comparisons , which equal . (Only the order matters for the syllabus; whether the bound is attained exactly for every is not needed.) The same method gives other lower bounds, for example that finding the maximum of keys needs comparisons: every key except the maximum must lose at least one comparison.
What the bound does not cover. An algorithm that uses the values of the keys (as an array index, as a digit) is not a comparison sort. The bound says nothing about it.
Counting sort
The animation shows the three passes on keys between and : count each key, add up the counts so that becomes the position just after the last key , then place the keys backwards. The code is a real run.
Counting sort: count the keys, add up the counts, place the keys backwards
Real runC source
Input a
count[v] for v = 0 to 4
Output out
Phase
Blue = the position this step is working on, amber = a position being compared, red = positions just swapped, green = finished or in its final place, a filled blue cell = a value just placed, faded = not part of the structure at this step.
Step 1
Sort the keys 3, 1, 4, 1, 3, 0, 4, 1, which are all between 0 and 4, with counting sort.
Cost. The three loops run , and times: time, and extra space (the count array of cells and the output array). It is linear when , but if is much larger than (for example ) it is worse than a comparison sort. It makes no comparison between keys. It is stable: scanning backwards puts the last of several equal keys last, and equal keys end up in input order.
Radix sort
Sorting by the least significant digit first and then by each more significant digit works because each pass is stable: after the pass for digit the numbers are ordered by their last digits, since a stable sort by digit keeps the order by the lower digits among numbers with equal digit . The program sorts eight 3-digit numbers with one counting-sort pass per digit.
#include <stdio.h>
/* one stable counting-sort pass on the decimal digit that has the value place (1, 10, 100) */
static void pass_by_digit(int a[], int n, int place) {
int count[10] = {0}, out[16];
for (int i = 0; i < n; i++) count[(a[i] / place) % 10]++;
for (int d = 1; d < 10; d++) count[d] += count[d - 1];
for (int i = n - 1; i >= 0; i--) out[--count[(a[i] / place) % 10]] = a[i];
for (int i = 0; i < n; i++) a[i] = out[i];
}
int main(void) {
int a[8] = {493, 217, 826, 305, 118, 652, 270, 903};
printf("start ");
for (int i = 0; i < 8; i++) printf(" %3d", a[i]);
printf("\n");
int place = 1;
for (int d = 1; d <= 3; d++, place *= 10) {
pass_by_digit(a, 8, place);
printf("digit %d ", d);
for (int i = 0; i < 8; i++) printf(" %3d", a[i]);
printf("\n");
}
return 0;
}start 493 217 826 305 118 652 270 903
digit 1 270 652 493 903 305 826 217 118
digit 2 903 305 217 118 826 652 270 493
digit 3 118 217 270 305 493 652 826 903Radix sort, one digit at a time: start
Computed by a script, not typed| stage | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| start | ||||||||
| after digit 1 (units) | ||||||||
| after digit 2 (tens) | ||||||||
| after digit 3 (hundreds) |
Step 0
Least significant digit first: each pass is a stable counting sort on one decimal digit. After the pass for digit d the numbers are sorted by their last d digits.
In the first pass and have the same units digit and stay in the order ; that is stability at work.
Cost. Each of the passes is a counting sort with : in total. Choosing the base: numbers each below can be written with digits in base , so radix sort takes passes of : , linear for constant . For example integers in take two passes in base , while plain counting sort would take . For -bit keys and digits of bits the cost is .
If an unstable digit sort were used, the order produced by earlier passes would be destroyed and the result could be wrong. (Most-significant-digit-first variants exist; they recurse on groups of equal leading digits and do not need the same stability argument.)
Bucket sort
If the keys are drawn independently and uniformly from , each bucket receives about one key. With insertion sort inside the buckets the expected time is : the expected cost of a bucket with keys is and the expected value of for a bucket is a constant (about ). The worst case is (all keys fall into one bucket) if insertion sort is used inside. It is stable if the buckets are stable lists and the sort inside is stable. It depends on the distribution of the input, not only on .
Summary.
| Sort | Time | Extra space | Conditions |
|---|---|---|---|
| Any comparison sort | worst and average | only comparisons allowed | |
| Counting sort | integer keys in | ||
| Radix sort | digits in base , stable digit sort | ||
| Bucket sort | expected , worst | keys uniform on an interval |
Worked examples
EX 1Minimum comparisons for small n
EasyFor keys there are orders, so at least comparisons are needed in the worst case, and suffice (the exhaustive search of the next paragraph confirms it). For keys: , , and comparisons are indeed enough (a search over all strategies confirms that is the exact minimum). For keys: orders, bound .
EX 2Counting sort by hand
EasyKeys (). Counts for : . Prefix sums: . Now scan from the last key: the last key goes to position and the count of becomes ; the next key goes to position ; to position ; and so on, ending with the output . The three equal s were placed last-to-first, so they appear in their input order.
EX 3Choosing the base for radix sort
MediumSort integers, each less than . In base there are digits: passes of . In base there are digits (): passes of , about steps of the counting loops, against about in base (constants ignored). Plain counting sort would need a count array of cells. A comparison sort needs comparisons: the same order as radix sort here, since is not far from .
EX 4Why the digit sort must be stable
MediumSort by least-significant-digit radix sort. The units pass (stable) leaves the order (units ). The tens pass must keep the order of numbers with the same tens digit: stable, it gives , which is sorted. An unstable tens pass is free to put before and before in other ways, for example , which is not sorted: the order established by the units pass was lost. The stability of each later pass carries the work of the earlier ones.
EX 5Which statements hold?
GATE level(a) Any comparison-based sort needs comparisons in the worst case: true. (b) Merge sort is an optimal comparison sort: true, asymptotically. (c) Counting sort breaks the lower bound: it is not a comparison sort, so it does not contradict it. (d) Sorting integers in in time: yes, radix sort with base and three counting-sort passes. (e) Counting sort with : , worse than . (f) Radix sort with an unstable digit sort: may fail. (g) Bucket sort's worst case: with insertion sort inside the buckets.
Cheat box
Cheat box
- Decision tree: leaves, height (worst and average). Merge and heap sort are optimal.
- Counting sort: keys , time and space, stable (backward pass), no comparisons.
- Radix sort (LSD): , needs a stable digit sort; keys below : base , passes, .
- Bucket sort: uniform keys, expected , worst .
- Minimum comparisons to sort keys: .
- Finding the maximum needs comparisons.
Traps and GATE patterns
Connections
Builds on
Across subjects
No cross-subject links yet.
The upper bounds that match this lower bound are merge sort and heap sort; quicksort matches it on average (quicksort). Stability, which makes radix sort correct, is compared across algorithms in stability and comparison of sorts. The idea of using the key value as an index is the idea of hashing.
PYQs
No PYQs attached to this concept yet
Previous year questions are being added section by section from your PYQ book, each with my own re-checked answer. Nothing is shown from memory.
Self-check
Question 1
What is the minimum number of comparisons needed in the worst case to sort distinct keys?
Show answerHide
At least , and is exactly the minimum (found by exhaustive search).
Question 2
Why does counting sort not contradict the bound?
Show answerHide
It is not a comparison sort: it uses the key values as array indices and never compares two keys.
Question 3
How many passes and what total time does radix sort need for integers in ?
Show answerHide
Two passes in base , each a counting sort of : in all.
Question 4
What goes wrong if radix sort uses an unstable digit sort?
Show answerHide
Equal digits can be reordered, destroying the order established by the less significant digits, so the final array may not be sorted.
Revision card
One-screen revision
- Comparison sort: decision tree with leaves, height ; worst and average; : exact.
- Counting: , stable. Radix: , LSD, stable passes; base for keys below .
- Bucket: expected uniform, worst .
- Not comparison based: no contradiction.
References
- ·Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (3rd edition), Chapter 8 Sorting in Linear Time (8.1 lower bounds, 8.2 counting sort, 8.3 radix sort, 8.4 bucket sort)
- ·Sedgewick and Wayne, Algorithms (4th edition), Section 2.5 and Section 5.1 String Sorts (key-indexed counting, LSD radix sort)
- ·Knuth, The Art of Computer Programming, Volume 3, Section 5.3.1 Minimum-comparison sorting; Section 5.2.5 Sorting by distribution
Verification
The decision tree bound was backed by an exhaustive game search (every strategy of comparing pairs, for every set of consistent orders): the exact minimum worst-case number of comparisons for n = 1 to 5 is 0, 1, 3, 5, 7, equal to ceil(log2 n!) in each case; the table of the bound was recomputed with exact integers, and the Stirling-type inequalities were checked numerically. Counting sort was tested on every array of up to 6 keys from {0, 1, 2, 3} against a stable reference; radix sort on 500 random inputs, including the claim that keys below n^3 need three passes in base n; the radix table was replayed by independent code. The expected linear time of bucket sort for uniform keys was measured by simulation; its proof is the standard one from the textbook. Textbook chapter references are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (loop counts, recurrences, merge and quicksort counts and averages, hash probes and load-factor formulas, DP tables, MST and shortest-path runs, Floyd matrices); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)