1. Home
  2. Design & Analysis of Algorithms
  3. Counting Sort & Radix Sort

Counting Sort & Radix Sort

Sorting without comparing. Counting sort uses values as positions, and radix sort repeats it digit by digit, beating the O(n log n) limit.

Interactive 3DIntermediate12 min readDAAUpdated

Drag to rotate · Right-drag to pan · Click, then scroll to zoom · Space play · ←→ step

What's happening

Pseudocode

    Try this in the 3D model

    • Run counting sort. After the prefix-sum step, what does count[3] mean?
    • Watch the two 0s and the three 3s. Do equal values keep their original order?
    • Switch to radix sort. After the first pass, why are 802 and 2 next to each other?
    • Try radix sort on numbers with different lengths, for example 7, 450, 12, 999, 30.

    Breaking the n log n barrier

    Every sort that works by comparing two elements (bubble, merge, quick, heap…) needs at least about n log n comparisons in the worst case. That is a proven lower bound.

    Counting sort and radix sort get around it by never comparing elements. They use the values themselves as array positions. This only works when the values are small whole numbers, or can be split into small digits.

    Counting sort

    For values between 0 and k:

    1. Count how many times each value appears.
    2. Turn the counts into positions with prefix sums: count[v] += count[v − 1]. Now count[v] is one past the last slot where v goes.
    3. Walk the input from right to left. For each x, decrement count[x] and write x at that position.

    Example with A = [2, 5, 3, 0, 2, 3, 0, 3] and k = 5:

    Value 0 1 2 3 4 5
    Count 2 0 2 3 0 1
    After prefix sums 2 2 4 7 7 8

    So the two 0s go to positions 0–1, the 2s to 2–3, the 3s to 4–6 and the 5 to position 7. The output is [0, 0, 2, 2, 3, 3, 3, 5].

    Walking from right to left makes counting sort stable: equal values stay in their original order.

    Radix sort

    What if the numbers go up to 999, or are 10-digit phone numbers? A count array that big is wasteful. Radix sort sorts by one digit at a time, using a stable counting sort (or 10 buckets) for each digit, starting from the least significant digit:

    Pass Sorted by List after the pass
    start – 170, 45, 75, 90, 802, 24, 2, 66
    1 ones digit 170, 90, 802, 2, 24, 45, 75, 66
    2 tens digit 802, 2, 24, 45, 66, 170, 75, 90
    3 hundreds digit 2, 24, 45, 66, 75, 90, 170, 802

    Why does it work? After pass 2, numbers with the same tens digit are still ordered by their ones digit, because the pass was stable. Each pass adds one more correctly sorted digit.

    Code

    def counting_sort(a, k):
        count = [0] * (k + 1)
        for x in a:
            count[x] += 1
        for v in range(1, k + 1):
            count[v] += count[v - 1]          # end position of each value
        out = [0] * len(a)
        for x in reversed(a):                 # right to left: stable
            count[x] -= 1
            out[count[x]] = x
        return out
    
    def radix_sort(a):
        exp = 1
        while max(a) // exp > 0:              # one pass per digit
            buckets = [[] for _ in range(10)]
            for x in a:
                buckets[(x // exp) % 10].append(x)
            a = [x for b in buckets for x in b]
            exp *= 10
        return a
    
    print(counting_sort([2, 5, 3, 0, 2, 3, 0, 3], 5))   # [0, 0, 2, 2, 3, 3, 3, 5]
    print(radix_sort([170, 45, 75, 90, 802, 24, 2, 66]))  # [2, 24, 45, 66, 75, 90, 170, 802]
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    void radixSort(vector<int>& a) {
        int mx = *max_element(a.begin(), a.end());
        vector<int> out(a.size());
        for (int exp = 1; mx / exp > 0; exp *= 10) {
            int count[10] = {0};
            for (int x : a) count[(x / exp) % 10]++;
            for (int d = 1; d < 10; d++) count[d] += count[d - 1];
            for (int i = (int)a.size() - 1; i >= 0; i--)          // stable
                out[--count[(a[i] / exp) % 10]] = a[i];
            a = out;
        }
    }
    
    int main() {
        vector<int> a = {170, 45, 75, 90, 802, 24, 2, 66};
        radixSort(a);
        for (int x : a) cout << x << " ";   // 2 24 45 66 75 90 170 802
    }

    When to use them

    Counting sort Radix sort Comparison sorts
    Time O(n + k) O(d · (n + b)) O(n log n)
    Works on Small integer keys Integers, fixed-length strings Anything you can compare
    Stable ✅ ✅ Merge yes, quick/heap no
    Extra memory O(n + k) O(n + b) O(1) to O(n)

    Real uses: sorting exam marks (0–100), ages, pixel values (0–255), IP addresses and suffix arrays in text search engines. Radix sort is also popular on GPUs, where it sorts millions of keys in parallel.

    Common mistakes

    • Using counting sort when the range is huge, for example values up to 10⁹. The count array would be enormous.
    • Walking left to right in the placement step. The output is still sorted, but counting sort is no longer stable, and radix sort built on it breaks.
    • Starting radix sort with the most significant digit and treating it like LSD. MSD radix sort needs recursion per bucket.
    • Forgetting that numbers with fewer digits simply have a digit 0 in the higher places.

    Complexity at a glance

    Case / operationTimeWhy
    Counting sort (values 0 … k)O(n + k)One pass over the input, one over the counts.
    Radix sort (d digits, base b)O(d · (n + b))d stable counting sorts, one per digit.
    Any comparison sortΩ(n log n)The limit these algorithms avoid.
    Extra spaceO(n + k)

    Quick check

    Test yourself — pick an answer to see if you got it.

    1. Counting sort is a good choice when…

    2. Why does counting sort place items from right to left?

    3. In LSD radix sort, which digit is used first?

    4. Sorting 1,000,000 phone numbers of 10 digits with radix sort (base 10) takes about…

    Saved only in this browser — no account needed.
    Spotted a mistake or a bug in the 3D model?

    Report a mistake

    in Counting Sort & Radix Sort. Thank you — every report makes the lesson better for the next reader.

    We'll also include a link to the step of the 3D model you're on and your browser type, so we can reproduce it.