Back to Journal
02 / Entry· 2 min read

排序算法系列 10:桶排序——先分堆再各自整理

用快递分拣的思路理解桶排序,讲解分桶策略、桶内排序选择及与计数排序的关系

🔊 系统朗读

排序算法系列 10:桶排序——先分堆再各自整理

桶排序的思想特别朴素:一堆东西混在一起不好排,那就先按范围分成几堆,每堆数量少了就好办了。

你可以把它想象成快递分拣——全国快递不可能一件件从头排到尾,而是先按省份分堆,每个省内部再按城市排,最后顺序一拼就好了。

核心思想

  1. 把数据按值域范围分到若干个"桶"里
  2. 每个桶内部用其他排序算法(比如插入排序)排好
  3. 按桶的顺序把结果拼起来

桶排序的性能好不好,取决于一个关键因素:数据是不是均匀分布到各个桶里

如果数据很均匀,每个桶里就几个元素,桶内排序很快,总体接近线性。如果数据全挤在一个桶里,那就退化成桶内排序本身的复杂度了。

走一遍过程

[0.78, 0.17, 0.39, 0.26, 0.72, 0.94] 举例,数据范围 0~1,分 5 个桶。

分桶:

text
桶 0 [0.0, 0.2):0.17
桶 1 [0.2, 0.4):0.39, 0.26
桶 2 [0.4, 0.6):(空)
桶 3 [0.6, 0.8):0.78, 0.72
桶 4 [0.8, 1.0]:0.94

桶内排序:

text
桶 0:0.17
桶 1:0.26, 0.39
桶 3:0.72, 0.78
桶 4:0.94

合并:

text
0.17, 0.26, 0.39, 0.72, 0.78, 0.94

搞定。每个桶里最多两个元素,排起来几乎不花时间。

代码实现

javascript
function bucketSort(arr, bucketCount = 5) {
  if (arr.length <= 1) return [...arr];

  const min = Math.min(...arr);
  const max = Math.max(...arr);
  if (min === max) return [...arr];

  const buckets = Array.from({ length: bucketCount }, () => []);
  const range = max - min;

  // 分桶
  for (const num of arr) {
    const index = Math.min(
      bucketCount - 1,
      Math.floor(((num - min) / range) * bucketCount)
    );
    buckets[index].push(num);
  }

  // 桶内排序 + 合并
  const result = [];
  for (const bucket of buckets) {
    insertionSort(bucket);
    result.push(...bucket);
  }

  return result;
}

function insertionSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    const current = arr[i];
    let j = i - 1;
    while (j >= 0 && arr[j] > current) {
      arr[j + 1] = arr[j];
      j--;
    }
    arr[j + 1] = current;
  }
}

console.log(bucketSort([0.78, 0.17, 0.39, 0.26, 0.72, 0.94]));
// [0.17, 0.26, 0.39, 0.72, 0.78, 0.94]

桶内用插入排序是因为每个桶里元素通常很少,插入排序在这种小数据量下效率最高。

和计数排序什么关系?

计数排序其实可以看成桶排序的一种特殊情况:

计数排序桶排序
桶的粒度每个值一个桶一个范围一个桶
适用数据整数,范围小任意数值,分布均匀
桶内排序不需要(每桶只有相同值)需要

桶排序更通用,可以处理小数、浮点数、甚至字符串——只要你能设计出合理的分桶规则。

桶的数量怎么定?

没有标准答案,通常有几种策略:

  • 取数据量的平方根:Math.sqrt(n)
  • 固定数量(比如 5、10、20)
  • 根据业务含义分(比如分数段:0-59, 60-69, 70-79, 80-89, 90-100)

核心原则:让数据尽量均匀分布到各个桶里。桶太少,单桶数据太多;桶太多,空桶浪费空间。

复杂度

n 是数据量,k 是桶数量:

  • 理想情况(数据均匀分布):O(n + k),接近线性
  • 最坏情况(数据全在一个桶里):取决于桶内排序,用插入排序就是 O(n²)

空间复杂度 O(n + k)

稳定性取决于桶内排序——用插入排序的话是稳定的。

适用场景

  • 数据在某个范围内均匀分布
  • 可以自然划分区间(分数段、价格区间、时间段)
  • 数据量大但值域有限

不适合数据分布极度不均匀的场景——比如 99% 的数据集中在一个很小的范围内,那分桶基本没用。

下一篇来看基数排序——非比较排序的最后一个成员,它的思路更巧妙:按位排序。


系列导航

上一篇:排序算法系列 09:计数排序——不比较,直接数

下一篇:排序算法系列 11:基数排序——一位一位地排,最后自然有序

分享
← 返回博客列表
🎁 有邀请福利哦,点击查看
🎁