Purpose: Sort integers in deterministic time in the word RAM model, beating the comparison lower bound by exploiting the bit structure of the keys (Yijie Han, 2002).

The bound applies to comparison-based sorting, where the only permitted operation on keys is <. Integer sorting in the word RAM model allows arithmetic, bit operations, and indexing on -bit words in — a strictly more powerful model, so the lower bound does not apply.

Familiar examples: counting sort is and radix sort is ; neither compares keys.

The techniques

Han’s algorithm combines several word-RAM tricks:

  1. Packed sorting. Fit several small keys into one machine word and sort them all with a constant number of word operations (a bitonic network implemented with masks, shifts, and one multiplication).
  2. Signature sorting. Replace each key by a short hash “signature” so that many keys fit in a word; sort signatures, then repair the small number of ties.
  3. Fusion trees (Fredman-Willard) — a -tree with branching factor , where each node compares against keys in via a “sketch and multiply” trick. Gives sorting on its own.
  4. Range reduction. Recursively halve the key length, which is where the comes from.

The integer sorting landscape

AlgorithmTimeSpaceNotes
Comparison sort-the lower bound in its model
Counting sortonly for small key range
Radix sort (base )what you actually write
Fusion treesFredman-Willard 1993
Handeterministic
Han-Thorup expectedrandomized; current best
Open?still unresolved

What is actually usable

None of these. But the ideas are:

  • Radix sort genuinely beats std::sort by 3-5× on large arrays of 32-bit integers. An LSD radix sort with 8-bit digits is 25 lines and does four counting-sort passes. Worth having in a template library for problems with .
  • Packing several values into one word and operating on them in parallel is the same idea as bitset optimization — a speedup that turns into something passable.
  • Sorting by a cheap key first, then repairing ties is a useful general pattern (it is also how prefix doubling sorts suffixes).

Radix sort, concretely

void radixSort(vector<uint32_t>& a) {
    vector<uint32_t> b(a.size());
    for (int shift = 0; shift < 32; shift += 8) {
        int cnt[257] = {};
        for (uint32_t x : a) cnt[((x >> shift) & 255) + 1]++;
        for (int i = 0; i < 256; i++) cnt[i + 1] += cnt[i];
        for (uint32_t x : a) b[cnt[(x >> shift) & 255]++] = x;
        swap(a, b);
    }
}

Variants / Use Cases