• Home
  • About
    • Thoughts To Pen photo

      Thoughts To Pen

      My thoughts on Computer Programming || Psychology || Personal Finances || & much more...

    • Learn More
    • Twitter
    • Instagram
    • Github
    • StackOverflow
  • Posts
    • All Posts
    • All Tags
  • Projects
  • Portfolio
  • Resources
  • About

Counting Sort, Radix Sort & Bucket Sort

04 Feb 2026

Introduction

Every sorting algorithm we have studied so far — Bubble Sort, Merge Sort, Quick Sort — works by comparing elements. There is a mathematical proof that comparison-based sorting cannot do better than $O(n \log n)$ in the worst case.

But what if we don’t compare elements at all? What if we use the values themselves to decide where they go? When the data has certain properties (like integers within a known range), we can sort in linear time — $O(n)$.

This post covers three such algorithms:

  1. Counting Sort — count occurrences, then reconstruct.
  2. Radix Sort — sort digit by digit using Counting Sort as a helper.
  3. Bucket Sort — distribute into buckets, sort each bucket individually.

Counting Sort

How It Works

Imagine you are a teacher collecting exam scores that range from 0 to 10. Instead of sorting the score sheets directly, you create 11 boxes (labelled 0 through 10). You walk through the pile once, dropping each score into its matching box. Then you walk through the boxes in order and collect all the sheets. Done — sorted.

Algorithm Steps

  1. Find the range of values (minimum to maximum).
  2. Create a count array of size max - min + 1, initialised to zeros.
  3. Walk through the input array and increment count[element - min] for each element.
  4. Build the sorted output by repeating each value according to its count.

Step-by-Step Walkthrough

Sort [4, 2, 7, 1, 4, 2, 3] where values range from 1 to 7.

Step 1: Build the count array

Value 1 2 3 4 5 6 7
Count 1 2 1 2 0 0 1

Step 2: Reconstruct the sorted array

Read counts left to right: one 1, two 2s, one 3, two 4s, zero 5s, zero 6s, one 7.

Result: [1, 2, 2, 3, 4, 4, 7] ✅


Java Implementation

public class CountingSort {

    /**
     * Sorts an integer array using Counting Sort.
     * Assumes all values are non-negative integers.
     */
    public static void countingSort(int[] arr) {
        if (arr.length == 0) return;

        // Step 1: Find the maximum value to determine the count array size
        int max = arr[0];
        for (int num : arr) {
            if (num > max) max = num;
        }

        // Step 2: Build the count array
        int[] count = new int[max + 1];
        for (int num : arr) {
            count[num]++;
        }

        // Step 3: Reconstruct the sorted array
        int index = 0;
        for (int value = 0; value <= max; value++) {
            while (count[value] > 0) {
                arr[index] = value;
                index++;
                count[value]--;
            }
        }
    }

    public static void main(String[] args) {
        int[] data = {4, 2, 7, 1, 4, 2, 3};
        countingSort(data);
        // Output: [1, 2, 2, 3, 4, 4, 7]
        for (int num : data) System.out.print(num + " ");
    }
}

Complexity

Metric Value Explanation
Time $O(n + k)$ Where $k$ is the range (max − min). We walk the input once ($n$) and the count array once ($k$).
Space $O(k)$ For the count array.

When it breaks down: If the values have a huge range (e.g., 1 to 10,000,000) but you only have 100 elements, you waste memory on a giant count array.


Radix Sort

How It Works

Counting Sort is great but only for small value ranges. Radix Sort extends the idea by sorting one digit at a time, starting from the least significant digit (rightmost) to the most significant digit (leftmost), using a stable sort (like Counting Sort) at each step.

Think of it like sorting mail at a postal office. First, you sort all letters by the last digit of the postal code. Then you sort by the second-to-last digit (keeping the previous order for ties because the sort is stable). By the time you reach the first digit, everything is fully sorted.

Algorithm Steps

  1. Find the maximum number to know how many digits to process.
  2. For each digit position (ones, tens, hundreds, …):
    • Use Counting Sort on that digit.

Step-by-Step Walkthrough

Sort [170, 45, 75, 90, 802, 24, 2, 66].

Maximum value is 802 → 3 digits to process.

Pass 1 — Sort by ones digit

Number Ones Digit
170 0
45 5
75 5
90 0
802 2
24 4
2 2
66 6

After sorting by ones: [170, 90, 802, 2, 24, 45, 75, 66]

Pass 2 — Sort by tens digit

Number Tens Digit
170 7
90 9
802 0
02 0
24 2
45 4
75 7
66 6

After sorting by tens: [802, 2, 24, 45, 66, 170, 75, 90]

Pass 3 — Sort by hundreds digit

Number Hundreds Digit
802 8
002 0
024 0
045 0
066 0
170 1
075 0
090 0

After sorting by hundreds: [2, 24, 45, 66, 75, 90, 170, 802] ✅


Java Implementation

public class RadixSort {

    /**
     * Sorts an array of non-negative integers using Radix Sort (LSD).
     * Uses Counting Sort as the stable sub-routine for each digit.
     */
    public static void radixSort(int[] arr) {
        if (arr.length == 0) return;

        // Find the maximum number to determine how many digit passes we need
        int max = arr[0];
        for (int num : arr) {
            if (num > max) max = num;
        }

        // Process each digit: exp = 1 (ones), 10 (tens), 100 (hundreds), ...
        for (int exp = 1; max / exp > 0; exp *= 10) {
            countingSortByDigit(arr, exp);
        }
    }

    /**
     * Counting Sort on a specific digit position determined by exp.
     * For exp=1, sorts by ones digit; exp=10, sorts by tens; etc.
     */
    private static void countingSortByDigit(int[] arr, int exp) {
        int n = arr.length;
        int[] output = new int[n];
        int[] count = new int[10]; // digits 0–9

        // Count occurrences of each digit
        for (int num : arr) {
            int digit = (num / exp) % 10;
            count[digit]++;
        }

        // Convert counts to cumulative positions (for stability)
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }

        // Build the output array (process RIGHT to LEFT to maintain stability)
        for (int i = n - 1; i >= 0; i--) {
            int digit = (arr[i] / exp) % 10;
            output[count[digit] - 1] = arr[i];
            count[digit]--;
        }

        // Copy the output back into the original array
        System.arraycopy(output, 0, arr, 0, n);
    }

    public static void main(String[] args) {
        int[] data = {170, 45, 75, 90, 802, 24, 2, 66};
        radixSort(data);
        // Output: [2, 24, 45, 66, 75, 90, 170, 802]
        for (int num : data) System.out.print(num + " ");
    }
}

Complexity

Metric Value Explanation
Time $O(d \times (n + k))$ $d$ = number of digits, $k$ = range per digit (10 for decimal). For fixed-size integers, $d$ is constant, giving $O(n)$.
Space $O(n + k)$ For the output and count arrays.

Bucket Sort

How It Works

Imagine you have 100 marbles of different weights and a row of 10 boxes, each labelled with a weight range: Box 1 = 0–9g, Box 2 = 10–19g, etc. You drop each marble into the correct box, sort each box individually (maybe with Insertion Sort since each box has only ~10 items), and then concatenate all the boxes.

Algorithm Steps

  1. Create an array of empty “buckets” (lists).
  2. Distribute elements into buckets based on a formula (e.g., element * numBuckets / (max + 1)).
  3. Sort each bucket individually.
  4. Concatenate all buckets in order.

Step-by-Step Walkthrough

Sort [0.42, 0.32, 0.23, 0.52, 0.25, 0.47, 0.51] (values between 0 and 1).

Using 5 buckets:

Bucket Range Elements
0 [0.0, 0.2) —
1 [0.2, 0.4) 0.32, 0.23, 0.25
2 [0.4, 0.6) 0.42, 0.52, 0.47, 0.51
3 [0.6, 0.8) —
4 [0.8, 1.0) —

Sort each bucket individually:

  • Bucket 1 → [0.23, 0.25, 0.32]
  • Bucket 2 → [0.42, 0.47, 0.51, 0.52]

Concatenate: [0.23, 0.25, 0.32, 0.42, 0.47, 0.51, 0.52] ✅


Java Implementation

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class BucketSort {

    /**
     * Sorts an array of floats in [0, 1) using Bucket Sort.
     */
    public static void bucketSort(float[] arr) {
        int n = arr.length;
        if (n <= 0) return;

        // Create n empty buckets
        @SuppressWarnings("unchecked")
        List<Float>[] buckets = new ArrayList[n];
        for (int i = 0; i < n; i++) {
            buckets[i] = new ArrayList<>();
        }

        // Distribute elements into buckets
        for (float value : arr) {
            int bucketIndex = (int) (value * n); // maps [0,1) to bucket indices
            buckets[bucketIndex].add(value);
        }

        // Sort each bucket (Insertion Sort via Collections.sort for small lists)
        for (List<Float> bucket : buckets) {
            Collections.sort(bucket);
        }

        // Concatenate all buckets back into the original array
        int index = 0;
        for (List<Float> bucket : buckets) {
            for (float value : bucket) {
                arr[index++] = value;
            }
        }
    }

    public static void main(String[] args) {
        float[] data = {0.42f, 0.32f, 0.23f, 0.52f, 0.25f, 0.47f, 0.51f};
        bucketSort(data);
        // Output: [0.23, 0.25, 0.32, 0.42, 0.47, 0.51, 0.52]
        for (float num : data) System.out.print(num + " ");
    }
}

Complexity

Metric Best/Average Worst Explanation
Time $O(n + k)$ $O(n^2)$ Best when elements are uniformly distributed. Worst when everything falls into one bucket.
Space $O(n + k)$ — $k$ = number of buckets.

When to Use Each

Algorithm Best For Limitations
Counting Sort Small integer range (e.g. ages 0–120, grades 0–100) Useless for floating-point or huge ranges
Radix Sort Large integers, fixed-length strings, IP addresses Requires data that can be decomposed into digits/characters
Bucket Sort Uniformly distributed floating-point numbers Degrades to $O(n^2)$ if distribution is skewed

Practice Problems

  1. Sort an Array – LeetCode #912 — Try implementing Counting Sort or Radix Sort.
  2. Maximum Gap – LeetCode #164 — This problem specifically requires linear-time sorting (Radix or Bucket Sort).
  3. Top K Frequent Elements – LeetCode #347 — Can be solved elegantly with Bucket Sort.

What’s Next?

We have now covered all the fundamental sorting algorithms — from the simple $O(n^2)$ methods to the $O(n)$ linear-time specialists. Next, we shift focus to searching: Linear Search & Binary Search.



programmingalgorithmssorting Share Tweet Msg