Definition · Counting sortCounting sort sorts n records whose keys are integers in 0 … k−1. Tally the keys into an array of k counters. Replace the tally with its running totals, so that counter i holds the number of keys at most i. Then pass over the input from back to front, writing each record into the output slot its counter names and decrementing that counter.
- Tally: k counters zeroed, then one increment per record. Cost Θ(n + k).
- Prefix: one pass over the counters, replacing each with the running total. Cost Θ(k).
- Place: one pass over the records, back to front, one write each. Cost Θ(n).
Example · Twelve keys in the range zero to fourThe keys are 4 1 3 4 3 0 2 4 1 3 4 2. The tally is 1 2 2 3 4 — one zero, two ones, two twos, three threes, four fours — and the prefix sums are 1 3 5 8 12. Placing from the back gives 0 1 1 2 2 3 3 3 4 4 4 4 with the tags inside each run still ascending. The work is n + k = 17.
No branch in that procedure looked at two keys together. The comparison bound is silent, and the cost is the sum of two terms that have nothing to do with each other: n, the number of records, and k, the width of the declared key range.
The second term is the whole question
Θ(n + k) is called linear and is linear in n + k, which is not the same thing as linear in n. At 12 keys over a range of five, the work is 17 against n log₂ n ≈ 43, and counting sort wins comfortably. Declare a 16-bit key and the work is 12 + 65 536. Declare a 32-bit key and it is 4 294 967 308, and the counter array alone is four billion slots of memory for twelve records.
So the algorithm is chosen by a comparison of terms, not by a class. It is the right sort when k is O(n) or thereabouts: ages, bytes, digits, bucket indices, grades. It is the wrong sort when k is n², or a machine word, or unknown. The lab's range control only changes the second term, which is the term that decides.
Theorem · Backward placement is stableIf the placement pass visits the records from last to first, writing each into slot prefix[key] and then decrementing that counter, then records with equal keys appear in the output in the same relative order as in the input.
Fix a key value v, and let the records with that key be, in input order, r₁, r₂, …, r_t. Before any of them is placed, prefix[v] is the number of keys at most v, which is the last slot the run owns.
The pass visits them in the order r_t, r_{t−1}, …, r₁, and each visit takes the current value of prefix[v] and then decreases it by one. So r_t gets the last slot of the run, r_{t−1} the one before, and r₁ the first. The output order is r₁, …, r_t, which is the input order.
The same argument with the pass running forwards hands r₁ the last slot and r_t the first, reversing the run — which is not a bug in the bookkeeping but the same bookkeeping read the other way.
□
The lab runs both directions on the same keys and flags the reordered pairs: zero from the back, eleven from the front. The output is sorted either way. Only the order inside each run of equal keys differs, and that is precisely what radix sort needs, since it is d of these passes stacked and its correctness argument is nothing but their stability.
One more property is worth naming: counting sort is not in place. It needs n output slots and k counters, so it trades memory for the comparison it does not make. Heapsort was the other trade in the same two dimensions, and neither trade is implied by anything about Θ.