Radix sort
Radix sort sorts numbers one digit at a time, using a stable sort for each digit. Starting from the least significant digit (LSD) it needs only as many passes as the keys have digits.
Learning goals
- Run LSD radix sort pass by pass and explain why stability is essential
- Contrast LSD and MSD radix sort
- Choose a sensible base for integer keys
- State the O(d · (n + b)) cost
Prerequisites
Library cards were once sorted by machine one column at a time: first the last digit, then the one before it, and so on. After the first column is done, later columns can only reorder by their own digit, and a stable pass keeps the earlier order among ties.
LSD vs MSD
LSD (least significant digit first) processes digits from right to left. Each pass is a stable sort on one digit, and no recursion is needed. It requires keys of the same width, or padding.
MSD (most significant digit first) splits the data into buckets by the first digit and then recursively sorts each bucket by the next digit. It can stop early once a bucket has one element, and it is natural for variable-length strings, but it needs recursion and bookkeeping for many small buckets.
| LSD | MSD | |
|---|---|---|
| Direction | Right to left | Left to right |
| Structure | Iterative, fixed number of passes | Recursive on buckets |
| Needs stable pass | Yes, essential | Not across buckets |
| Good for | Fixed-width integers | Variable-length strings, early termination |
Digit buckets
For base 10, create ten buckets numbered 0-9. For each element, in input order, append it to the bucket given by the current digit (x / exp) % 10. Then concatenate the buckets from 0 to 9 back into the array. Repeat with exp = 1, 10, 100, … until max / exp is 0.
Watch the ten digit rows in the animation. After the final pass the array is sorted. The number of passes is the number of digits of the largest value.
Best
O(d · (n + b))
Average
O(d · (n + b))
Worst
O(d · (n + b))
Space
O(n + b)
LSD radix sort (base 10)
Interactive · edit the inputint[] arr · length 8
The largest value is 802, which has 3 digits, so 3 passes are needed.
1public static void radixSort(int[] arr) {2int max = max(arr);3for (int exp = 1; max / exp > 0; exp *= 10) { // least significant digit first4List<List<Integer>> bucket = new ArrayList<>();5for (int d = 0; d < 10; d++) bucket.add(new ArrayList<>());6for (int x : arr) bucket.get((x / exp) % 10).add(x); // stable distribute7int k = 0;8for (List<Integer> b : bucket)9for (int x : b) arr[k++] = x; // gather in digit order10}11}
Live state
max
802
Execution trace (dry run table)Show 43 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | max=802 | The largest value is 802, which has 3 digits, so 3 passes are needed. |
| 2 | L3 | exp=1 | Pass 1: sort by the ones digit (exp = 1). Ten empty buckets, one per digit 0-9. |
| 3 | L6 | x=170 exp=1 digit=0 | (170 / 1) % 10 = 0. Append 170 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 4 | L6 | x=45 exp=1 digit=5 | (45 / 1) % 10 = 5. Append 45 to bucket 5, behind anything that arrived earlier (this is what keeps each pass stable). |
| 5 | L6 | x=75 exp=1 digit=5 | (75 / 1) % 10 = 5. Append 75 to bucket 5, behind anything that arrived earlier (this is what keeps each pass stable). |
| 6 | L6 | x=90 exp=1 digit=0 | (90 / 1) % 10 = 0. Append 90 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 7 | L6 | x=802 exp=1 digit=2 | (802 / 1) % 10 = 2. Append 802 to bucket 2, behind anything that arrived earlier (this is what keeps each pass stable). |
| 8 | L6 | x=24 exp=1 digit=4 | (24 / 1) % 10 = 4. Append 24 to bucket 4, behind anything that arrived earlier (this is what keeps each pass stable). |
| 9 | L6 | x=2 exp=1 digit=2 | (2 / 1) % 10 = 2. Append 2 to bucket 2, behind anything that arrived earlier (this is what keeps each pass stable). |
| 10 | L6 | x=66 exp=1 digit=6 | (66 / 1) % 10 = 6. Append 66 to bucket 6, behind anything that arrived earlier (this is what keeps each pass stable). |
| 11 | L9 | digit=0 k=2 | Gather bucket 0: 170, 90. The array is now ordered by the ones digit. |
| 12 | L9 | digit=2 k=4 | Gather bucket 2: 802, 2. The array is now ordered by the ones digit. |
| 13 | L9 | digit=4 k=5 | Gather bucket 4: 24. The array is now ordered by the ones digit. |
| 14 | L9 | digit=5 k=7 | Gather bucket 5: 45, 75. The array is now ordered by the ones digit. |
| 15 | L9 | digit=6 k=8 | Gather bucket 6: 66. The array is now ordered by the ones digit. |
| 16 | L3 | exp=10 | Pass 2: sort by the tens digit (exp = 10). Ten empty buckets, one per digit 0-9. |
| 17 | L6 | x=170 exp=10 digit=7 | (170 / 10) % 10 = 7. Append 170 to bucket 7, behind anything that arrived earlier (this is what keeps each pass stable). |
| 18 | L6 | x=90 exp=10 digit=9 | (90 / 10) % 10 = 9. Append 90 to bucket 9, behind anything that arrived earlier (this is what keeps each pass stable). |
| 19 | L6 | x=802 exp=10 digit=0 | (802 / 10) % 10 = 0. Append 802 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 20 | L6 | x=2 exp=10 digit=0 | (2 / 10) % 10 = 0. Append 2 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 21 | L6 | x=24 exp=10 digit=2 | (24 / 10) % 10 = 2. Append 24 to bucket 2, behind anything that arrived earlier (this is what keeps each pass stable). |
| 22 | L6 | x=45 exp=10 digit=4 | (45 / 10) % 10 = 4. Append 45 to bucket 4, behind anything that arrived earlier (this is what keeps each pass stable). |
| 23 | L6 | x=75 exp=10 digit=7 | (75 / 10) % 10 = 7. Append 75 to bucket 7, behind anything that arrived earlier (this is what keeps each pass stable). |
| 24 | L6 | x=66 exp=10 digit=6 | (66 / 10) % 10 = 6. Append 66 to bucket 6, behind anything that arrived earlier (this is what keeps each pass stable). |
| 25 | L9 | digit=0 k=2 | Gather bucket 0: 802, 2. The array is now ordered by the tens digit. |
| 26 | L9 | digit=2 k=3 | Gather bucket 2: 24. The array is now ordered by the tens digit. |
| 27 | L9 | digit=4 k=4 | Gather bucket 4: 45. The array is now ordered by the tens digit. |
| 28 | L9 | digit=6 k=5 | Gather bucket 6: 66. The array is now ordered by the tens digit. |
| 29 | L9 | digit=7 k=7 | Gather bucket 7: 170, 75. The array is now ordered by the tens digit. |
| 30 | L9 | digit=9 k=8 | Gather bucket 9: 90. The array is now ordered by the tens digit. |
| 31 | L3 | exp=100 | Pass 3: sort by the hundreds digit (exp = 100). Ten empty buckets, one per digit 0-9. |
| 32 | L6 | x=802 exp=100 digit=8 | (802 / 100) % 10 = 8. Append 802 to bucket 8, behind anything that arrived earlier (this is what keeps each pass stable). |
| 33 | L6 | x=2 exp=100 digit=0 | (2 / 100) % 10 = 0. Append 2 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 34 | L6 | x=24 exp=100 digit=0 | (24 / 100) % 10 = 0. Append 24 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 35 | L6 | x=45 exp=100 digit=0 | (45 / 100) % 10 = 0. Append 45 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 36 | L6 | x=66 exp=100 digit=0 | (66 / 100) % 10 = 0. Append 66 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 37 | L6 | x=170 exp=100 digit=1 | (170 / 100) % 10 = 1. Append 170 to bucket 1, behind anything that arrived earlier (this is what keeps each pass stable). |
| 38 | L6 | x=75 exp=100 digit=0 | (75 / 100) % 10 = 0. Append 75 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 39 | L6 | x=90 exp=100 digit=0 | (90 / 100) % 10 = 0. Append 90 to bucket 0, behind anything that arrived earlier (this is what keeps each pass stable). |
| 40 | L9 | digit=0 k=6 | Gather bucket 0: 2, 24, 45, 66, 75, 90. The array is now ordered by the hundreds digit. |
| 41 | L9 | digit=1 k=7 | Gather bucket 1: 170. The array is now ordered by the hundreds digit. |
| 42 | L9 | digit=8 k=8 | Gather bucket 8: 802. The array is now ordered by the hundreds digit. |
| 43 | L11 | Sorted after 3 passs. Each pass is stable, so earlier (lower-digit) ordering survives inside equal higher digits. Cost O(d · (n + b)). |
Stable passes
Each pass must keep elements with the same current digit in their previous relative order. That previous order already reflects all the lower digits, so two numbers tied on the tens digit stay ordered by their ones digit.
If a pass were unstable, the work of the earlier passes would be lost. Appending to buckets in input order, or running counting sort right to left, both give stability. Try “Same last digit” to see 101, 201, 301 survive the first pass in input order.
Integers vs strings
Fixed-width integers suit LSD directly. Negative integers need care: either offset every value by the minimum, or sort the negative and non-negative values separately and reverse the negatives.
Strings of equal length work with LSD over characters. Strings of different lengths are better handled by MSD, treating a missing character as smaller than any real one, or by padding. Radix sort on strings costs O(total characters), compared with O(n log n) string comparisons that each cost up to the common prefix length.
1// LSD radix sort for equal-length lowercase words2static void radixSortStrings(String[] words, int length) {3for (int pos = length - 1; pos >= 0; pos--) {4int[] count = new int[27];5for (String w : words) count[w.charAt(pos) - 'a' + 1]++;6for (int c = 1; c < count.length; c++) count[c] += count[c - 1];7String[] out = new String[words.length];8for (String w : words) out[count[w.charAt(pos) - 'a']++] = w; // left to right with start offsets9System.arraycopy(out, 0, words, 0, words.length);10}11}
Base selection
With base b and d digits, the cost is O(d · (n + b)), where d = ⌈log_b(max + 1)⌉. A larger base means fewer passes but more buckets per pass. A 32-bit integer needs 10 passes in base 10, 4 passes in base 256, and 2 passes in base 65,536.
Base 256 is the usual choice: it extracts one byte per pass with shifts and masks, (x >> (8 * pass)) & 0xFF, and 256 counters fit easily in cache. Very large bases hurt because the bucket array no longer fits in cache.
| Base b | Passes for 32-bit ints | Buckets per pass |
|---|---|---|
| 10 | 10 | 10 |
| 256 | 4 | 256 |
| 65,536 | 2 | 65,536 |
Practice ladder
Now try it yourself
Easy
- Run radix sort by hand on [329, 457, 657, 839, 436, 720, 355]
- How many passes does base 10 need for the maximum 98,765?
Medium
- Implement LSD radix sort in base 256 for non-negative ints
- Radix sort for integers including negatives
- Maximum gap between sorted neighbours in O(n)
Advanced
- MSD radix sort for strings with an end-of-string bucket
- Sort 32-bit floats with radix sort by remapping their bit patterns
- Compare radix and quick sort on 10 million ints with JMH
Key takeaways
- LSD radix sort does one stable pass per digit; stability carries the order of lower digits forward.
- Cost is O(d · (n + b)); base 256 is the common practical choice for integers.
- MSD radix sort suits variable-length strings and can stop early.