Introduction

Every comparison sort needs Ω(nlg⁡n)\Omega(n \lg n) comparisons in the worst case (see Comparison of Sorting Algorithms). The three algorithms in this note beat that bound because they are not comparison sorts: they use the keys as array indices or split them into digits. Each works only under an assumption about the keys:

AlgorithmAssumption on keysTime
Counting sortintegers in 0..k0..kΘ(n+k)\Theta(n + k)
Radix sortdd digits, each in 0..k−10..k-1Θ(d(n+k))\Theta(d(n + k))
Bucket sortreal numbers drawn uniformly from [0,1)[0, 1)Θ(n)\Theta(n) expected

When k=O(n)k = O(n) and dd is a constant, all three are linear.

Prerequisites

  • Elementary Sorting: stability (essential here) and insertion sort (used inside bucket sort).
  • Arrays with 1-based indexing; counting arrays are indexed from 0.

Counting sort

Purpose. Sort nn integers in the range 0..k0..k stably. Input. A[1..n]A[1..n] with 0≤A[j]≤k0 \le A[j] \le k. Output. B[1..n]B[1..n], a sorted copy. Auxiliary: C[0..k]C[0..k].

Pseudocode uses 1-based arrays, <- for assignment and A[i..j] for the subarray from position ii to position jj.

Text
COUNTING-SORT(A, B, n, k)
    for v <- 0 to k
        C[v] <- 0
    for j <- 1 to n                    // count each key
        C[A[j]] <- C[A[j]] + 1
    for v <- 1 to k                    // C[v] = number of keys <= v
        C[v] <- C[v] + C[v - 1]
    for j <- n downto 1                // place, right to left
        B[C[A[j]]] <- A[j]
        C[A[j]] <- C[A[j]] - 1

Why it works. After the prefix sums, C[v]C[v] is the number of keys ≤v\le v, which is the last position a key vv can occupy in sorted order. Each placement uses that position and decrements it for the next equal key.

Why it is stable. The last loop scans AA from right to left, so among equal keys the rightmost is placed in the highest position. Equal keys keep their input order. Scanning left to right with the same decrementing CC would reverse them and lose stability: in the example, the two 0s would land as A[4]A[4] in B[2]B[2] and A[7]A[7] in B[1]B[1]. A left-to-right scan is stable only in the other common variant, where C[v]C[v] is first turned into the first position for key vv (the number of keys <v< v, plus 1) and is incremented after each placement.

Worked example

A=[2,5,3,0,2,3,0,3]A = [2, 5, 3, 0, 2, 3, 0, 3], n=8n = 8, k=5k = 5.

vv012345
CC after counting202301
CC after prefix sums224778

Placement from j=8j = 8 down: A[8]=3A[8] = 3 goes to B[7]B[7]; A[7]=0A[7] = 0 to B[2]B[2]; A[6]=3A[6] = 3 to B[6]B[6]; A[5]=2A[5] = 2 to B[4]B[4]; A[4]=0A[4] = 0 to B[1]B[1]; A[3]=3A[3] = 3 to B[5]B[5]; A[2]=5A[2] = 5 to B[8]B[8]; A[1]=2A[1] = 2 to B[3]B[3]. Result B=[0,0,2,2,3,3,3,5]B = [0, 0, 2, 2, 3, 3, 3, 5].

Counting sort arrays A, C and B
Figure 1. Counting sort on 2 5 3 0 2 3 0 3 with k=5k = 5: the input AA, the count array CC before and after prefix sums, and the output BB, with arrows for the first three placements (A[8]A[8], A[7]A[7], A[6]A[6]).

Analysis

  • Time Θ(n+k)\Theta(n + k): two loops over nn, two over kk. Linear when k=O(n)k = O(n); poor when k≫nk \gg n (for example sorting 10 numbers below 10910^9).
  • Space Θ(n+k)\Theta(n + k) auxiliary (arrays BB and CC): not in place.
  • Best, average and worst cases are the same: it never compares keys.
  • Stable; works for records with integer keys, not only for bare integers.
  • Negative keys or keys in a..ba..b: shift by −a-a and use k=b−ak = b - a.

Radix sort

Purpose. Sort keys made of dd digits by sorting on one digit at a time, using a stable sort for each digit.

Text
RADIX-SORT(A, d)
    for i <- 1 to d                     // digit 1 is the least significant
        use a stable sort to sort A on digit i

This is least significant digit (LSD) radix sort. It must start from the least significant digit, and the per-digit sort must be stable: after sorting on digit ii, keys that tie on digit ii stay ordered by digits 1..i−11..i-1 from the earlier passes. By induction, after pass ii the keys are sorted on their last ii digits.

Worked example

Seven 3-digit numbers, sorted with stable passes on units, tens and hundreds:

InputAfter unitsAfter tensAfter hundreds
329720720329
457355329355
657436436436
839457839457
436657355657
720329457720
355839657839

In the tens pass, 720 and 329 have tens digits 2 and 2; they stay in their units-pass order (720 before 329) because the pass is stable. In the final pass the hundreds digits decide.

LSD radix sort passes
Figure 2. LSD radix sort of 329 457 657 839 436 720 355: four columns (input, after units, after tens, after hundreds), with the digit used in each pass emphasised.

Analysis

  • With counting sort as the stable per-digit sort and digits in 0..k−10..k-1: each pass is Θ(n+k)\Theta(n + k), so the total is Θ(d(n+k))\Theta(d(n + k)). Linear when dd is constant and k=O(n)k = O(n).
  • Choosing the digit size. For integer 1≤r≤b1\le r\le b and nn keys of bb bits, split each key into ⌈b/r⌉\lceil b/r \rceil digits of rr bits; then k=2rk = 2^r and the time is Θ(⌈b/r⌉(n+2r))\Theta(\lceil b/r\rceil(n + 2^r)). Taking r≈lg⁡nr \approx \lg n (for b≥lg⁡nb \ge \lg n) gives Θ(bn/lg⁡n)\Theta(bn / \lg n). Example: n=216n = 2^{16} keys of b=32b = 32 bits with r=16r = 16 need 2 passes with k=65536k = 65536.
  • Space Θ(n+k)\Theta(n + k) with counting sort; not in place.
  • Stable (if every pass is stable).
  • Most significant digit first works only with recursion into each bucket (MSD radix sort); running plain passes from the most significant digit gives a wrong result. On the worked example, stable passes on hundreds, then tens, then units end with 720 355 436 457 657 329 839: the last pass wins, so the list is ordered primarily by the units digit.
  • For n≥2n\ge2 and a fixed integer c≥1c\ge1, integers 0≤x<nc0\le x<n^c can be sorted in Θ(n)\Theta(n) time: write them in base nn, using cc digits in 0..n−10..n-1.

Bucket sort

Purpose. Sort nn real numbers that are drawn independently and uniformly from [0,1)[0, 1).

Text
BUCKET-SORT(A, n)
    create empty lists B[0..n-1]
    for i <- 1 to n
        insert A[i] into list B[floor(n * A[i])]
    for i <- 0 to n - 1
        sort list B[i] with insertion sort
    concatenate B[0], B[1], ..., B[n-1] in order

Example. A=[0.78,0.17,0.39,0.26,0.72,0.94,0.21,0.12,0.23,0.68]A = [0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12, 0.23, 0.68], n=10n = 10. Bucket ⌊10x⌋\lfloor 10x \rfloor: bucket 1 gets 0.17, 0.12; bucket 2 gets 0.26, 0.21, 0.23; bucket 3 gets 0.39; bucket 6 gets 0.68; bucket 7 gets 0.78, 0.72; bucket 9 gets 0.94; buckets 0, 4, 5 and 8 stay empty. After sorting each bucket and concatenating: 0.12 0.17 0.21 0.23 0.26 0.39 0.68 0.72 0.78 0.94.

Bucket sort of ten values
Figure 3. Bucket sort of the ten example values: an array of ten buckets with each value linked into bucket ⌊10x⌋\lfloor 10x \rfloor, each bucket shown sorted, and the concatenated output.

Analysis

  • Distribution and concatenation: Θ(n)\Theta(n).
  • If nin_i is the size of bucket ii, insertion sort costs O(ni2)O(n_i^2). Under the uniform assumption, E[ni2]=2−1/nE[n_i^2] = 2 - 1/n, so the expected total is Θ(n)+n⋅O(2−1/n)=Θ(n)\Theta(n) + n \cdot O(2 - 1/n) = \Theta(n).
  • Worst case Θ(n2)\Theta(n^2): if all keys fall into one bucket, insertion sort runs on all nn. Using merge sort inside buckets lowers the worst case to O(nlg⁡n)O(n \lg n).
  • Space Θ(n)\Theta(n) for the buckets. Stable if insertion into buckets appends and the bucket sort is stable.
  • The linear bound holds more generally whenever the sum of the squares of the bucket sizes is linear in expectation; with a non-uniform but known distribution, choose bucket boundaries at its quantiles.

Comparison

PropertyCounting sortRadix sort (LSD)Bucket sort
Key assumptionintegers 0..k0..kdd digits in 0..k−10..k-1uniform on [0,1)[0, 1)
TimeΘ(n+k)\Theta(n + k) all casesΘ(d(n+k))\Theta(d(n + k)) all casesΘ(n)\Theta(n) expected, Θ(n2)\Theta(n^2) worst
Auxiliary spaceΘ(n+k)\Theta(n + k)Θ(n+k)\Theta(n + k)Θ(n)\Theta(n)
Stableyes (right-to-left placement)yes (needs stable passes)yes, if bucket sort is stable
In placenonono
Comparison-basednonopartly (inside buckets)

Important properties and observations

  • These algorithms do not contradict the Ω(nlg⁡n)\Omega(n \lg n) lower bound: it applies only to algorithms that learn about keys by comparisons.
  • Counting sort is the standard stable subroutine for radix sort. Replacing it by an unstable sort (for example, quicksort on each digit) breaks radix sort.
  • "Linear" depends on the parameters: counting sort with k=n2k = n^2 is Θ(n2)\Theta(n^2); radix sort with d=Θ(lg⁡n)d = \Theta(\lg n) digits and k=O(n)k=O(n) is Θ(nlg⁡n)\Theta(n \lg n); in general its cost is Θ(d(n+k))\Theta(d(n+k)).
  • Counting sort can also count, for each value, how many keys are ≤\le it: range-count queries in O(1)O(1) after Θ(n+k)\Theta(n + k) preprocessing.

GATE examination perspective

  • Counting sort traces. the count array CC after counting and after prefix sums, and the position each element lands in. Remember the right-to-left final loop.
  • Radix sort traces. the list after a given pass; each pass must keep ties in their previous order.
  • Complexity with parameters. express time in nn, kk, dd or bb and rr; decide whether it is linear.
  • Traps. radix sort needs a stable digit sort and must go from the least significant digit (the last pass decides the primary order); counting sort's final loop must run right to left when CC holds last positions; bucket sort's linear time is an expectation under the uniform assumption.

Summary

Sources

  1. T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Introduction to Algorithms, 3rd ed., MIT Press, 2009: Chapter 8, Sections 8.2 (counting sort), 8.3 (radix sort) and 8.4 (bucket sort and its expected-time analysis).

Next steps