Chapter 18: Efficient and Specialized Sorting
18.06Beginner / Intermediate4 topics12 min1 interactive animation

Bucket sort

Bucket sort distributes values into buckets by range, sorts each small bucket, and concatenates the buckets. When the input is spread evenly it runs in linear expected time.

Learning goals

  • Map a value to its bucket with arithmetic
  • Sort each bucket and concatenate them
  • Explain how the distribution of the input decides the running time

Prerequisites

Insertion sortLists in JavaCounting sort

To sort a pile of letters by name, put them into labelled trays A-C, D-F and so on, then sort each small tray by hand. Each tray holds only a few letters, so the work stays small as long as the letters spread over the trays.

18.06.1

Distribution assumptions

Bucket sort assumes the input values are drawn roughly uniformly from a known range, such as real numbers in [0, 1) or integers in [0, 100). Under that assumption each of n buckets receives about one element.

If the assumption fails and most values land in a few buckets, the buckets become the bottleneck and the algorithm is only as fast as the algorithm used inside the buckets.

18.06.2

Bucket construction

For n values in [0, 100) use n buckets and the index x * n / 100. For doubles in [0, 1) use (int) (x * n). The index is computed directly from the value, with no comparisons between elements.

Append each element to its bucket in input order. Use a linked list or ArrayList for each bucket. The animation labels each bucket with the range of values it covers.

Java
1static void bucketSort(double[] a) { // values in [0, 1)
2 int n = a.length;
3 List<List<Double>> buckets = new ArrayList<>();
4 for (int i = 0; i < n; i++) buckets.add(new ArrayList<>());
5 for (double x : a) buckets.get((int) (x * n)).add(x);
6 for (List<Double> b : buckets) Collections.sort(b);
7 int k = 0;
8 for (List<Double> b : buckets)
9 for (double x : b) a[k++] = x;
10}

Bucket sort

Interactive · edit the input

Your input

Try

int[] arr · length 10

78
17
39
26
72
94
21
12
23
68
0123456789
Being readBeing placedDone

bucket 0 [0-9]

empty

bucket 1 [10-19]

empty

bucket 2 [20-29]

empty

bucket 3 [30-39]

empty

bucket 4 [40-49]

empty

bucket 5 [50-59]

empty

bucket 6 [60-69]

empty

bucket 7 [70-79]

empty

bucket 8 [80-89]

empty

bucket 9 [90-99]

empty
Setup

10 buckets, each covering a slice of [0, 100). Bucket index = x · n / 100, so a value's bucket is decided by arithmetic, not by comparisons.

Step 1 / 24
Java
1public static void bucketSort(int[] arr) { // values in [0, 100)
2 int n = arr.length;
3 List<List<Integer>> buckets = new ArrayList<>();
4 for (int i = 0; i < n; i++) buckets.add(new ArrayList<>());
5 for (int x : arr) buckets.get(x * n / 100).add(x);
6 for (List<Integer> b : buckets) Collections.sort(b);
7 int k = 0;
8 for (List<Integer> b : buckets)
9 for (int x : b) arr[k++] = x;
10}

Live state

0 comparisons

n

10

No condition evaluated on this step
Execution trace (dry run table)Show 24 steps
StepLineStateWhat happens
1L2n=1010 buckets, each covering a slice of [0, 100). Bucket index = x · n / 100, so a value's bucket is decided by arithmetic, not by comparisons.
2L5x=78 bucket=778 · 10 / 100 = 7. Append it to bucket 7.
3L5x=17 bucket=117 · 10 / 100 = 1. Append it to bucket 1.
4L5x=39 bucket=339 · 10 / 100 = 3. Append it to bucket 3.
5L5x=26 bucket=226 · 10 / 100 = 2. Append it to bucket 2.
6L5x=72 bucket=772 · 10 / 100 = 7. Append it to bucket 7.
7L5x=94 bucket=994 · 10 / 100 = 9. Append it to bucket 9.
8L5x=21 bucket=221 · 10 / 100 = 2. Append it to bucket 2.
9L5x=12 bucket=112 · 10 / 100 = 1. Append it to bucket 1.
10L5x=23 bucket=223 · 10 / 100 = 2. Append it to bucket 2.
11L5x=68 bucket=668 · 10 / 100 = 6. Append it to bucket 6.
12L6bucket=1 size=2Sort bucket 1 (17, 12) with insertion sort: 1 comparison. Small buckets are cheap to sort.
13L6bucket=2 size=3Sort bucket 2 (26, 21, 23) with insertion sort: 3 comparisons. Small buckets are cheap to sort.
14L6bucket=3 size=1Bucket 3 holds one value, nothing to sort.
15L6bucket=6 size=1Bucket 6 holds one value, nothing to sort.
16L6bucket=7 size=2Sort bucket 7 (78, 72) with insertion sort: 1 comparison. Small buckets are cheap to sort.
17L6bucket=9 size=1Bucket 9 holds one value, nothing to sort.
18L9bucket=1 k=2Copy bucket 1 back into arr[0..1].
19L9bucket=2 k=5Copy bucket 2 back into arr[2..4].
20L9bucket=3 k=6Copy bucket 3 back into arr[5..5].
21L9bucket=6 k=7Copy bucket 6 back into arr[6..6].
22L9bucket=7 k=9Copy bucket 7 back into arr[7..8].
23L9bucket=9 k=10Copy bucket 9 back into arr[9..9].
24L10Sorted. The fullest bucket holds 3 values. If values spread evenly each bucket stays tiny and the total is O(n); if they all land in one bucket it degrades to the cost of the inner sort.
18.06.3

In-bucket sort

Each bucket is sorted separately. Insertion sort is the traditional choice, because buckets are small and insertion sort has almost no overhead on a handful of elements. Collections.sort also works, and it is stable.

Stability of the whole algorithm follows from two things: elements enter each bucket in input order, and the sort inside the bucket is stable.

18.06.4

Expected complexity

Distributing costs O(n), concatenating costs O(n + number of buckets), and sorting the buckets costs the sum of cost(bucket size). For uniform data the expected bucket size is constant, so the expected total is O(n + k) with k buckets.

In the worst case every element falls into one bucket, which costs the inner sort on n elements: O(n²) with insertion sort, or O(n log n) with a merge-based sort. Compare the “Even spacing (ideal)” preset with “Clustered (worst case)” and read the size of the fullest bucket.

Input shapeLargest bucketTotal time (insertion sort inside)
Uniform spreadabout 1-3 elementsO(n)
Moderately skeweda few elementsbetween O(n) and O(n²)
All in one bucketn elementsO(n²)

Best

O(n + k)

Average

O(n + k)

Worst

O(n²)

Space

O(n + k)

Practice ladder

Now try it yourself

Easy

  • Sort an array of doubles in [0, 1) with bucket sort
  • Sort test scores 0-100 using 10 buckets

Medium

  • Top K frequent elements with buckets indexed by frequency
  • Sort strings by their first letter into 26 buckets, then within each bucket

Advanced

  • Choose bucket boundaries from sample quantiles for non-uniform data
  • Maximum gap between consecutive sorted values in linear time using buckets

Key takeaways

  • Bucket sort maps each value to a bucket by arithmetic, sorts each bucket, then concatenates.
  • Expected time is O(n + k) for uniformly distributed input; the worst case is the inner sort on all n elements.
  • It is stable when buckets are filled in input order and sorted with a stable sort.