<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. DSA
  3. Searching and sorting
All lessons in this course

Lesson 17 of 50 13 minIntermediate

Counting sort and radix sort

Why comparison sorts cannot beat O(n log n), and how counting sort and radix sort beat it anyway for small integer keys.

Every sort so far works by comparing two items. It can be proved that any comparison-based sort needs about n log n comparisons in the worst case. There are n! possible orders of n items, and each yes/no comparison can at best halve the remaining possibilities, so you need log₂(n!) ≈ n log₂ n comparisons to identify the right one.

To go faster you must stop comparing and use the values themselves. That only works when the keys are small whole numbers (or can be split into them), but when they are, the speed-up is real.

Counting sort

If every value is between 0 and k − 1, make an array of k counters. One pass counts how often each value appears. A running total turns counts into positions (“values ≤ 3 fill the first 7 slots”). A final pass places each item directly into its slot.

Interactive

Sorting by counting

No comparisons at all: count how often each value appears, turn the counts into positions, then place every item.

Input
4134021431
count[value]
0010203040
Output
··········
Phase: count
Values are small whole numbers 0..4. Make one counter per possible value.
Radix sort: a stable pass per digit, starting from the ones
input170045075090802024002066
by ones170090802002024045075066
by tens802002024045066170075090
by hundreds002024045066075090170802
CountingSort.cs
static int[] CountingSort(int[] a){    if (a.Length == 0) return a;    int min = a.Min(), max = a.Max();    var count = new int[max - min + 1];          // k = range of values     foreach (int v in a) count[v - min]++;       // 1. count     for (int i = 1; i < count.Length; i++)       // 2. running totals = end positions        count[i] += count[i - 1];     var output = new int[a.Length];    for (int i = a.Length - 1; i >= 0; i--)      // 3. place, right to left = stable        output[--count[a[i] - min]] = a[i];    return output;} Console.WriteLine(string.Join(" ", CountingSort([4, 1, 3, 4, 0, 2, 1, 4, 3, 1])));Console.WriteLine(string.Join(" ", CountingSort([3, -2, 0, 3, 1])));
Output
0 1 1 1 2 3 3 4 4 4
-2 0 1 3 3

Time O(n + k), extra space O(n + k). When k is small compared with n (exam marks 0 to 100, floor numbers, ages, status codes) this beats any comparison sort. When k is huge (values up to a billion), the counter array becomes the problem.

Radix sort

Radix sort handles large numbers by sorting on one digit at a time, starting with the least significant digit (LSD). Each pass is a stable counting sort with only 10 possible keys (0 to 9). After the ones pass, the tens pass, and the hundreds pass, the numbers are fully sorted, because the stable passes keep the earlier digit order inside each group.

RadixSort.cs
// LSD radix sort for non-negative integers, base 10static void RadixSort(int[] a){    if (a.Length == 0) return;    int max = a.Max();    var output = new int[a.Length];     for (int exp = 1; max / exp > 0; exp *= 10)          // 1, 10, 100, ...    {        var count = new int[10];        foreach (int v in a) count[v / exp % 10]++;        for (int d = 1; d < 10; d++) count[d] += count[d - 1];        for (int i = a.Length - 1; i >= 0; i--)          // stable            output[--count[a[i] / exp % 10]] = a[i];        Array.Copy(output, a, a.Length);    }} int[] data = { 170, 45, 75, 90, 802, 24, 2, 66 };RadixSort(data);Console.WriteLine(string.Join(" ", data));
Output
2 24 45 66 75 90 170 802

With d digits and base b, radix sort is O(d × (n + b)). For 32-bit integers, real implementations use base 256 (one byte per pass): just 4 passes over the data, whatever n is.

AlgorithmTimeWorks forStable
Comparison sorts (merge, quick)O(n log n)Anything you can compareMerge: yes, quick: no
Counting sortO(n + k)Small integer range kYes
Radix sortO(d × (n + b))Integers, fixed-length stringsYes
Bucket sortO(n) on averageValues spread evenly over a known rangeDepends
Quick check

You must sort 10 million survey answers, each an integer from 1 to 5. Which sort is best?

Quick check

Why must each pass of LSD radix sort be stable?

Practice

Sort the letters of a lowercase word with counting sort: "algorithms" → "aghilmorst". Use an array of 26 counters.

Show a hint

count[c - 'a']++ for each character, then append each letter count[i] times. StringBuilder.Append(char, repeatCount) helps (add using System.Text;).

Show the solution
Solution.cs
using System.Text; static string SortLetters(string s){    var count = new int[26];    foreach (char c in s) count[c - 'a']++;     var sb = new StringBuilder(s.Length);    for (int i = 0; i < 26; i++)        sb.Append((char)('a' + i), count[i]);    return sb.ToString();} Console.WriteLine(SortLetters("algorithms"));
Output
aghilmorst

Remember

  • Comparison sorts cannot beat about n log n comparisons in the worst case.
  • Counting sort counts each value and places items directly: O(n + k) for keys in a range of k.
  • Radix sort applies a stable counting sort digit by digit: O(d × (n + b)).
  • These sorts only work for integer-like keys; for general objects use a comparison sort.
0 of 50 lessons completed