BeeTool.Dev - Essential Tools for developers

Stories.Tool Tabs VaultPersonal Documents
Super Debugging Context
BeeTool.Dev

Essential tools, knowledge, and playgrounds for developers.

Explore

  • Stories
  • Personal Documents
  • Features
  • Algorithms Playground

Tool Tabs

  • JSON Compare
  • SQL Formatter
  • Text Diff
  • All tools →

Learn

  • Sorting algorithms
  • Searching algorithms
  • Algorithm Race Mode
© 2024 – 2026 BeeTool.DevPrivacy policy
← Algorithms Playground
Sorting · Intermediate

Bucket Sort

Scatter values into range-based buckets, sort each tiny bucket, and concatenate — linear time when data spreads evenly.

AverageO(n + k)StableYesIn-placeNoComparisonNo
Must remember

Scatter by range into k buckets, insertion-sort each bucket, concatenate.

Best O(n+k) · Avg O(n+k) · Worst O(n²) · Space O(n+k)

Hands on Interactive visualization

Size 40
Values
UnsortedComparingSwap / writeSortedPivot
0/0Ready — press play
Speed
Step 0 / 0
Comparisons
0
Swaps
0
Reads
0
Writes
0
Elapsed
0.0s
Frames
0
Depth
0 / 0
Aux memory
0 B

Implementation Code

export function bucketSort(a: number[]): number[] {
  const n = a.length;
  if (n < 2) return a;
  const lo = Math.min(...a);
  const hi = Math.max(...a);
  if (lo === hi) return a; // all keys equal: nothing to do
  const span = hi - lo + 1;
  // k = n buckets: expected one element per bucket on uniform data.
  const buckets: number[][] = Array.from({ length: n }, () => []);
  for (const v of a) {
    const idx = Math.floor(((v - lo) * n) / span); // hi -> last bucket
    buckets[idx].push(v);
  }
  let pos = 0;
  for (const b of buckets) {
    // Insertion sort per bucket: tiny, and keeps the sort stable.
    for (let k = 1; k < b.length; k++) {
      const key = b[k];
      let j = k - 1;
      while (j >= 0 && b[j] > key) {
        b[j + 1] = b[j];
        j--;
      }
      b[j + 1] = key;
    }
    for (const v of b) a[pos++] = v; // concatenate in bucket order
  }
  return a;
}

Big-O Complexity

Best
O(n + k)
Average
O(n + k)
Worst
O(n²)
Space
O(n + k)

k = bucket count. Average case assumes roughly uniform key distribution; skewed data can pile everything into one bucket, degrading to the inner sort's worst case.

StableYesIn-placeNoComparisonNoRecursiveNoAdaptiveNoOnlineNo

In production When to use

Numeric / simulation

The textbook case: sorting floats uniformly distributed in [0, 1) — random samples, normalized scores, probabilities — in expected linear time.

Big Data

The scatter phase is exactly how distributed sorts shard work: range-partition keys across nodes (buckets), sort locally, concatenate. TeraSort-style benchmarks are bucket sort at cluster scale.

Graphics / Game dev

Depth binning: dropping renderables into a fixed array of depth slices approximates a sort in O(n) per frame, with exact sorting only inside each slice if needed.

Database

Histogram-based range partitioning in query engines and index builds is bucket sort's scatter step: cheap statistics pick bucket boundaries so partitions come out balanced.

Streaming analytics

Bucketing values by range (latency histograms, percentile sketches) gives approximately ordered data with O(1) placement per element — often good enough without the final per-bucket sort.

Trade-offs Strengths & weaknesses

  • Expected O(n) on uniformly distributed keys — with k ≈ n, buckets average one element each.
  • Naturally parallel and distributable: buckets are independent by construction.
  • Works on floats and any key with a cheap value-to-bucket mapping — unlike counting sort's integer requirement.
  • Stable when the scatter preserves order and the inner sort is stable (e.g. insertion sort).
  • Bucket boundaries can encode domain knowledge (histograms, quantiles) for balanced work.
  • Distribution-sensitive: skewed data lands in one bucket and performance collapses to O(n²) with an insertion-sort inner loop.
  • O(n + k) extra memory for buckets plus scattered allocation churn.
  • Choosing bucket count and boundaries well requires knowing the data's range and shape.
  • Poor cache locality during scatter: writes hop across k growing lists.
  • Not adaptive, not online, and useless when the key range is unknown up front.

Intuition Mental model

Sorting mail into carrier routes

A mailroom does not compare every letter to every other letter. It throws each letter into the tray for its neighborhood, then each carrier orders their own small tray.

  1. Set out one tray per neighborhood (bucket per key range).
  2. Toss every letter into its tray by address — a single O(1) decision per letter, no comparisons across trays.
  3. Each carrier sorts their own tray; with even neighborhoods, each tray is tiny and quick.
  4. Concatenate trays in route order — the whole mailbag is now sorted.
  5. If one neighborhood gets all the mail (skew), that carrier does nearly all the original work alone — the O(n²) failure mode.

Prep Interview questions

When is bucket sort O(n), and what assumption does the proof lean on?
With n buckets over uniformly distributed keys, the expected bucket size is 1 and the expected total inner-sort cost is O(n). The uniformity assumption is doing all the work — break it and the bound breaks.
Bucket sort vs counting sort — what exactly is the difference?
Counting sort needs one counter per distinct key value (k = key range) and never sorts within a counter. Bucket sort maps ranges of values per bucket and must sort within each bucket — it trades exactness of the map for handling floats and wide ranges.
How do you defend bucket sort against skewed distributions?
Pick boundaries from data, not from the theoretical range: sample the input, use quantiles/histograms for boundaries, or recursively bucket oversized buckets (which drifts toward MSD radix sort).
Why is insertion sort the usual inner sort rather than quicksort?
Expected bucket size is O(1) — a handful of elements — where insertion sort's constants dominate any asymptotic edge, and its stability keeps the whole bucket sort stable.
How does distributed sorting (e.g. TeraSort) relate to bucket sort?
Identical structure at cluster scale: sample keys to choose range boundaries, scatter records to nodes by range, sort per node, concatenate node outputs. Balance depends on the sampled boundaries — same skew risk.

Watch out Common bugs

Maximum value mapped one past the last bucket

A naive index formula sends v == max to bucket k, which does not exist. Clamp the index to k - 1 or derive the formula so the top value lands inside the last bucket.

Integer overflow in the bucket-index formula

(v - lo) * k can overflow fixed-width integers for wide ranges, producing negative indices. Widen the intermediate type or divide first.

Unstable inner sort silently breaking stability

The scatter preserves input order within each bucket; sorting buckets with an unstable algorithm throws that away. Use insertion sort (or another stable sort) if callers rely on stability.

Degenerate range when all keys are equal

max == min makes the bucket-width computation divide by zero. Detect the single-value case and return early.

One screen Cheat sheet

Scatter by range into k buckets, insertion-sort each bucket, concatenate.

Best O(n+k) · Avg O(n+k) · Worst O(n²) · Space O(n+k)
  • Expected linear only under (roughly) uniform key distribution.
  • k ≈ n buckets targets one element per bucket on average.
  • Skew is the killer: one hot bucket degrades to the inner sort's worst case.
  • Stable if scatter order is preserved and the inner sort is stable.
  • The blueprint for distributed range-partitioned sorting.
  • Data-derived boundaries (quantiles) are the practical skew defense.
Pick whenKeys are roughly uniform over a known range — floats in [0, 1), balanced numeric scores — and O(n) memory is available.
Avoid whenThe distribution is unknown or skewed, memory is tight, or keys lack a cheap value-to-bucket mapping.

Radix Sort

Tim Sort