Radix sort — LSD, MSD; O(d(n+k))
WHAT is Radix Sort?
Key vocabulary:
- = number of elements.
- = the radix / number of possible digit values (e.g. for decimal, for bytes).
- = number of digit positions = .
WHY does LSD radix sort work? (Derivation from scratch)
We need an invariant. Claim:
After processing digit positions (least significant first), the array is sorted by the number formed by those low digits.
Proof by induction.
Base case (): we stably sort by digit 0. The array is sorted by the lowest digit. ✓
Inductive step: Suppose after pass the array is sorted by digits . In pass we stably sort by digit .
- Two keys with different digit end in the correct relative order, because the higher digit dominates the value.
- Two keys with the same digit keep their previous relative order — because the sort is stable — and that previous order was already correct on digits . ✓
So after all passes the array is fully sorted. ∎
HOW: the algorithm (LSD with counting sort per digit)
def lsd_radix(a, k=10):
if not a: return a
maxv = max(a)
exp = 1 # 1, k, k^2, ... selects the digit
while maxv // exp > 0: # loop runs d times
a = counting_sort_by_digit(a, exp, k)
exp *= k
return a
def counting_sort_by_digit(a, exp, k):
n = len(a)
out = [0]*n
count = [0]*k
for x in a: # 1) tally this digit
count[(x // exp) % k] += 1
for i in range(1, k): # 2) prefix sums -> end positions
count[i] += count[i-1]
for x in reversed(a): # 3) place RIGHT-to-LEFT => STABLE
d = (x // exp) % k
count[d] -= 1
out[count[d]] = x
return out
WHY the running time is (Derivation)
One counting-sort pass costs:
- tally loop:
- prefix-sum loop:
- placement loop:
So one pass . We do exactly passes.
When is this beating ? If keys are bounded so is constant (e.g. 32-bit ints with ⇒ ) and , then — linear.
MSD vs LSD
| LSD | MSD | |
|---|---|---|
| Direction | right → left | left → right |
| Structure | flat loop, passes | recursive, divide into buckets |
| Always examines all digits? | Yes | No — can stop early on distinguishing prefix |
| Stable? | Yes | Yes (if buckets kept stable) |
| Best for | fixed-width ints | variable-length keys / strings, partial sort |
| Touches whole array each pass | Yes | Only the relevant bucket |

Worked Example 1 — LSD on , base 10
Pass exp=1 (units digit):
digits → 0,5,5,0,2,4,2,6. Stable counting sort gives
[170, 90, 802, 2, 24, 45, 75, 66].
Why this step? We sort by the least significant digit first so later passes (which dominate) can
rely on stability to preserve this order among ties.
Pass exp=10 (tens digit):
digits → 7,9,0,0,2,4,7,6 → [802, 2, 24, 45, 66, 170, 75, 90].
Why this step? Among keys with equal tens digit (170 & 75 both have tens 7) the previous unit
order is preserved by stability.
Pass exp=100 (hundreds digit):
digits → 8,0,0,0,0,1,0,0 → [2, 24, 45, 66, 75, 90, 170, 802]. ✅ Sorted.
Why this step? Max value 802 has 3 digits, so passes suffice; after the top digit the array
is fully ordered.
Worked Example 2 — MSD on strings ["bat","apple","ant","bad"]
Sort by char 0: bucket a = {apple, ant}, bucket b = {bat, bad}.
Why this step? The most significant character splits the data; buckets are independent.
Recurse in bucket a on char 1: both have n/p... actually "apple"[1]=p, "ant"[1]=n
→ ant before apple. Bucket a = [ant, apple].
Why this step? Only these two elements are touched — MSD localizes work.
Recurse in bucket b on char 1: "bat"[1]=a, "bad"[1]=a (tie) → recurse char 2: d < t
→ [bad, bat].
Why this step? We descend only as deep as needed to break ties.
Concatenate: [ant, apple, bad, bat]. ✅
Worked Example 3 — choosing the radix for 32-bit ints
Keys up to . Choose (sort one byte per pass). passes. Cost for large . Why this step? Bigger → fewer passes () but bigger count array. balances both; the count array (256 ints) is negligible vs .
Active Recall
Recall Feynman — explain to a 12-year-old
Imagine sorting a stack of player cards by a 3-digit number. You make 10 trays labelled 0–9. First you drop each card into the tray matching its last digit, then scoop the trays back up in order 0→9 (keeping cards within a tray in the same order). Repeat using the middle digit, then the first digit. After three rounds the cards are magically in order — and you never once compared two cards directly! The trick: always keep ties in the order they were already in (that's "stable"), and the earlier rounds quietly stay sorted.
Flashcards
Radix sort core idea
Why must the per-digit sort be stable
LSD processing direction
MSD processing direction
Time complexity of radix sort
Space complexity
Formula for d
Cost of one counting-sort pass
When does radix sort run in true O(n)
Why radix can be worse than quicksort
LSD vs MSD: which reads all digits
Best use of MSD
Why place elements right-to-left in counting sort
Connections
- Counting Sort — the stable engine inside each radix pass.
- Comparison Sort Lower Bound — the wall radix sidesteps.
- Stability in Sorting — the property the correctness proof relies on.
- Bucket Sort — cousin that distributes by value range instead of by digit.
- Big-O Notation — interpreting and the hidden .
- Tries — MSD radix sort is essentially building a trie level by level.
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho, normal sorting (quicksort, mergesort) mein hum do numbers ko compare karte hain "kaun bada?" — aur isi compare wajah se woh kabhi bhi se tez nahi ho sakte. Radix sort ka jugaad alag hai: woh numbers ko digit ke hisaab se sort karta hai, ek baar mein sirf ek digit. Har digit ke liye ek stable counting sort chalta hai. Stable ka matlab — agar do numbers ka current digit same hai, toh unka pehle wala order waisa ka waisa rehta hai. Yahi stability sab kuch sambhalti hai.
LSD matlab sabse aakhri (units) digit se shuru karo, fir tens, fir hundreds. Har pass ke baad array us digit tak sorted ho jaata hai, aur kyunki sort stable hai, purane passes ka kaam kharab nahi hota. MSD ulta hai — sabse bade (left) digit se shuru, fir har bucket ke andar recursion. MSD ka faayda: strings aur alag-alag length ke keys mein, agar pehla hi letter alag hai toh baaki padhne ki zarurat hi nahi — kaam jaldi khatam.
Time complexity hai: digits ki ginti, elements, radix (base). Ek pass ka hota hai aur aise passes. Yaad rakho — yeh "always linear" nahi hai. Agar numbers bahut bade ho gaye toh bada ho jaata hai aur phir wahi wapas. Isliye ko ke aas-paas chuno (jaise 32-bit int ko 1-byte chunks mein, , sirf 4 passes) — tab radix sort sach mein linear aur fast nikalta hai.