For numeric keys in a known, bounded range, magnitude can identify a range slice directly. Bucket Sort divides that range into m equal-width buckets, maps each key with one arithmetic calculation, sorts within each bucket, then concatenates the buckets in range order.

The mapping works because the bounds are known. bucketIndex = floor(m · (key − min) / (upperExclusive − min)) locates a bucket without inspecting another element. Performance depends on the buckets remaining small, which usually means the keys are spread roughly uniformly. When most keys land in a few buckets, the inner sort does nearly all the work.

Visualization

The middle range [0.4, 0.6) stays empty, while [0.6, 0.8) receives 0.78 and 0.72 and sorts them as 0.72, 0.78. Empty and occupied buckets gather the same way: range order already determines their order relative to every other bucket.

Complexity
  • Worst: Θ(n² + m)
n
number of values distributed into buckets
m
number of buckets

The average curve assumes a known, roughly uniform distribution and about as many buckets as elements. Skew can collapse distinct reverse-ordered values into one bucket and expose the inner sort’s quadratic behavior; duplicate-heavy occupancy alone does not, because equal keys trigger no shifts. The chart’s space curves include both bucket headers and stored elements, separate from the input array.

Replacing the stable insertion sort inside each bucket with an O(s log s) inner sort such as introsort, where s is the bucket size, improves the skewed tail but may change stability. Bucket Sort still returns the right order under skew because range partitioning remains valid; only the work inside an overloaded bucket changes.

The value-to-index mapping restricts the input. Keys need a numeric or otherwise orderable half-open range with known bounds. Opaque identifiers and values ordered only by an external comparator have no meaningful bucket index. They need a comparison sort or a digit-wise scheme such as Radix Sort.

Stability comes from the inner sort. Scatter preserves read order within each bucket, and gather does not mix buckets. Using Insertion Sort therefore keeps the whole algorithm stable. Replacing it with List<T>.Sort, an Introsort, can reorder equal keys when they carry associated data.

Diagram and C# Implementation

References