BeeTool.Dev - Essential Tools for developers

Stories.Tool Tabs VaultPersonal Documents
Super Debugging Context
BeeTool.Dev

Essential tools, knowledge, and playgrounds for developers.

Explore

  • Stories
  • Personal Documents
  • Features
  • Algorithms Playground

Tool Tabs

  • JSON Compare
  • SQL Formatter
  • Text Diff
  • All tools →

Learn

  • Sorting algorithms
  • Searching algorithms
  • Algorithm Race Mode
© 2024 – 2026 BeeTool.DevPrivacy policy
← Algorithms Playground
Sorting · Advanced

Radix Sort

Sort digit by digit with a stable pass per position — linear-time sorting for fixed-width keys.

AverageO(d · (n + k))StableYesIn-placeNoComparisonNo
Must remember

Stable counting sort per digit, least significant digit first.

Best O(d·(n+k)) · Avg O(d·(n+k)) · Worst O(d·(n+k)) · Space O(n+k)

Hands on Interactive visualization

Size 40
Values
UnsortedComparingSwap / writeSortedPivot
0/0Ready — press play
Speed
Step 0 / 0
Comparisons
0
Swaps
0
Reads
0
Writes
0
Elapsed
0.0s
Frames
0
Depth
0 / 0
Aux memory
0 B

Implementation Code

// LSD radix sort, base 10, for non-negative integers.
// (Handle negatives by offsetting with the min value first.)
export function radixSort(a: number[]): number[] {
  if (a.length === 0) return a;
  const max = Math.max(...a);
  let buf = new Array<number>(a.length);
  for (let exp = 1; Math.floor(max / exp) > 0; exp *= 10) {
    const counts = new Array<number>(10).fill(0);
    for (const v of a) counts[Math.floor(v / exp) % 10]++;
    // Prefix sums: counts[d] = index just past digit d's bucket.
    for (let d = 1; d < 10; d++) counts[d] += counts[d - 1];
    // Reverse scan keeps each pass stable — mandatory for LSD radix.
    for (let i = a.length - 1; i >= 0; i--) {
      const d = Math.floor(a[i] / exp) % 10;
      buf[--counts[d]] = a[i];
    }
    for (let i = 0; i < a.length; i++) a[i] = buf[i];
  }
  return a;
}

Big-O Complexity

Best
O(d · (n + k))
Average
O(d · (n + k))
Worst
O(d · (n + k))
Space
O(n + k)

d = digits per key, k = radix (bucket count). For fixed-width integers d and k are constants, making it effectively O(n).

StableYesIn-placeNoComparisonNoRecursiveNoAdaptiveNoOnlineNo

In production When to use

Big Data

Sorting massive volumes of fixed-width keys — 32/64-bit IDs, timestamps, IPv4 addresses — where d·n passes beat n log n comparisons once n is in the tens of millions.

Database

Column stores and join engines radix-partition fixed-width key columns; radix-based partitioning is a standard building block in modern hash-join and sort implementations.

Graphics / GPU

GPU radix sort is the de facto standard for particle depth ordering and building bounding-volume hierarchies from Morton codes — its counting passes parallelize almost perfectly.

Networking

Ordering packets, flows, or routing entries by fixed-width addresses and ports: byte-per-pass radix sort gives predictable linear throughput.

Strings

MSD radix sort and three-way radix quicksort are the classic algorithms for large sets of strings, examining each character at most once per key prefix.

Trade-offs Strengths & weaknesses

  • Effectively linear time for fixed-width keys — asymptotically beats comparison sorts.
  • Stable, inheriting stability from its counting-sort passes.
  • No comparisons and no data-dependent branching in the hot loop.
  • Deterministic throughput regardless of key distribution or input order.
  • Counting passes vectorize and parallelize well — the GPU sorting champion.
  • Only works on keys decomposable into digits: integers, fixed-width strings, bit-castable floats.
  • O(n) auxiliary buffer plus per-pass count arrays — never in-place in practice.
  • d full passes over the data hurt cache efficiency versus one quicksort pass structure.
  • Loses to quicksort when keys are long relative to log n (few elements, wide keys).
  • Signed and floating-point keys need bit tricks (sign flip / order-preserving transforms) that are easy to get wrong.

Intuition Mental model

The post office pigeonhole wall

Mail sorters order letters by ZIP code one digit at a time: distribute into ten pigeonholes by a digit, gather in order, repeat with the next digit — never comparing two letters directly.

  1. Distribute all letters into pigeonholes 0–9 by the last digit of the ZIP code.
  2. Gather the pigeonholes in order, carefully keeping each stack's internal order — that is stability.
  3. Redistribute by the next digit to the left; ties on this digit stay in the order the previous round produced.
  4. After one round per digit, the entire mailbag is sorted end to end.
  5. Total work: number of digits × one pass over every letter — no letter ever compared to another.

Prep Interview questions

Radix sort is O(d·n) and quicksort is O(n log n) — when does radix actually win?
When d < log n, roughly. Sorting 100M 32-bit ints with 4 byte-passes beats ~27 comparison levels. Sorting 100 wide UUIDs, it does not. Constants and memory traffic decide the boundary in practice.
Why must each digit pass be stable, and what breaks if it is not?
LSD radix relies on earlier (less significant) digit orderings surviving later passes: after sorting by digit i, ties on digit i+1 must keep that order. An unstable pass scrambles all previous work.
LSD vs MSD radix sort — when would you choose each?
LSD is simple, iterative, and ideal for fixed-width keys. MSD recurses per bucket, supports variable-length keys (strings), and can stop early on distinguishing prefixes — at the cost of recursion bookkeeping.
How do you radix-sort signed integers or floats correctly?
Map keys to an order-preserving unsigned form: flip the sign bit for signed ints; for IEEE floats, flip all bits of negatives and just the sign bit of positives. Sort the transformed bits, then invert.
Why is base 256 the usual choice over base 10?
A byte per pass means d = 4 for 32-bit keys and digit extraction becomes a shift + mask, with a 256-entry count array that fits in L1 cache. Bigger radixes trade fewer passes for larger, cache-hostile count tables.

Watch out Common bugs

Unstable per-digit pass

Implementing the digit pass with an unstable sort (or a forward placement scan) destroys the ordering established by earlier digits — output looks almost sorted, and tests on small inputs may even pass.

Negative numbers sorted as huge unsigned values

Bit-shifting a two's-complement negative puts it above all positives. Either offset by min first, flip the sign bit, or bucket negatives separately and reverse.

Wrong pass count for the actual key width

Hardcoding 4 byte-passes for values that are 64-bit (or stopping at a fixed digit count in base 10) leaves high digits unsorted. Derive d from the maximum key.

Reusing the count array without zeroing it

Each digit pass needs fresh counts. Forgetting to reset the array between passes accumulates stale tallies and scatters elements out of bounds.

One screen Cheat sheet

Stable counting sort per digit, least significant digit first.

Best O(d·(n+k)) · Avg O(d·(n+k)) · Worst O(d·(n+k)) · Space O(n+k)
  • Effectively O(n) for fixed-width integer keys — d and k are constants.
  • Every digit pass must be stable or the algorithm is simply wrong.
  • Base 256 (byte passes) is the practical choice: shift + mask, L1-resident counts.
  • Negatives and floats need order-preserving bit transforms first.
  • Wins over comparison sorts when d < log n — huge n, narrow keys.
  • GPU-friendly: counting passes parallelize almost perfectly.
Pick whenTens of millions of fixed-width integer-like keys and memory for an O(n) buffer.
Avoid whenKeys are arbitrary comparables, very wide relative to n, or memory for a second buffer is unavailable.

Counting Sort

Bucket Sort