基础排序算法(九)桶排序
一 桶排序
桶排序(Bucket Sort)是一种巧妙且高效的非比较排序算法,它通过将数据分到有限数量的有序“桶”中,分别对每个桶进行排序,最后合并结果来完成整体排序
1.1 算法特性
| 特性 | 描述 |
|---|---|
| 核心思想 | 分治与映射:将待排序元素通过映射函数分配到有限数量的桶中,每个桶内部排序后,再按桶顺序合并 |
| 时间复杂度 | 最好情况 O(n):数据均匀分布在各个桶中 平均情况 O(n + k):k为桶的数量 最坏情况 O(n²):所有数据集中在一个桶内 |
| 空间复杂度 | O(n + k),需要额外的空间存储桶和桶内元素 |
| 稳定性 | 稳定(取决于桶内排序算法的稳定性,通常使用稳定的插入排序) |
| 主要优势 | 在数据分布均匀且范围已知时,效率极高(接近线性时间) |
| 主要劣势 | 对数据分布敏感;需要额外的内存空间 |
1.2 算法工作原理
桶排序的运作过程清晰地分为“分桶”、“桶内排序”和“合并”三大阶段。下图以数组 [29, 25, 3, 49, 9, 37, 21, 43]为例,假设我们创建5个桶,每个桶负责一个数值范围(如0-9, 10-19等),展示了其完整的排序过程:

- 设置桶并确定映射规则:
- 首先确定待排序数据的最大值和最小值,计算数据的范围。
- 根据数据范围设定桶的数量和每个桶负责的区间。例如,对于范围在0-49的数据,可以设置5个桶,每个桶负责10个数的区间:桶0(0-9)、桶1(10-19),以此类推。映射函数通常为 f(value) = (value - min) / bucket_range,用来确定每个元素应放入哪个桶 。
- 遍历数据并放入桶中:
- 遍历整个待排序数组,根据映射函数将每个元素放入对应的桶中。
- 对每个桶内部进行排序:
- 对每个非空的桶,使用一种合适的排序算法对其内部的元素进行排序。通常可以选择插入排序,因为对于小数据量其效率不错且是稳定排序 。其他如快速排序、归并排序也可用。
- 按顺序合并桶:
- 最后,按照桶的编号顺序(从小到大),依次将每个桶中的元素取出,放回到原始数组中,此时整个数组便是有序的了。
1.3 复杂度分析
桶排序的性能高度依赖于数据的分布情况。
1.3.1 时间复杂度:
- 最好情况 O(n):当数据均匀分布在各个桶中时,每个桶内的元素数量接近,桶内排序时间最短,总体效率最高。
- 平均情况 O(n + k):k代表桶的数量。需要遍历n个元素分桶,然后对k个桶进行排序操作。
- 最坏情况 O(n²):当所有数据都被映射到同一个桶中时,桶排序退化为单一的桶内排序。如果桶内排序使用插入排序等O(n²)算法,最坏时间复杂度即为O(n²)。
1.3.2 空间复杂度 O(n + k):
- 算法需要额外的空间来存储k个桶,以及桶内的n个元素。
1.4 使用场景与限制
桶排序在特定场景下表现卓越,但也有其局限性。
-
理想应用场景:
- 数据分布均匀:待排序数据在一个已知范围内均匀分布时,效率最高。
- 数据范围已知:例如对年龄、考试成绩、价格等在固定范围内的数据进行排序。
- 外部排序:当数据量太大无法全部装入内存时,可以分批读入数据分桶排序,再合并结果。
-
主要限制:
- 数据分布敏感:如果数据分布极度不均,大部分数据集中在少数桶内,效率会显著下降。
- 需要额外空间:桶的数量和待排序元素数量会占用额外内存。
- 数据范围未知或极大:如果数据范围很大但数据量很小,创建大量空桶会非常浪费空间。
1.5 代码实现
1.5.1 c 语言实现
#include <stdio.h>
#include <stdlib.h>
#define BUCKET_COUNT 10 // 假设我们使用10个桶,每个桶负责一个10个数的区间(如0-9, 10-19...90-99)
#define BUCKET_SIZE 10 // 假设每个桶最多容纳10个元素
// 插入排序函数,用于对每个桶内的元素进行排序
void insertionSort(int arr[], int n) {
int i, j, key;
for (i = 1; i < n; i++) {
key = arr[i];
j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
// 桶排序主函数
void bucketSort(int arr[], int n) {
int i, j, index;
// 创建桶,这是一个二维数组
int buckets[BUCKET_COUNT][BUCKET_SIZE];
// 记录每个桶中当前存放的元素个数
int bucketElementsCount[BUCKET_COUNT] = {0};
// 1. 将数组中的元素分配到各个桶中
for (i = 0; i < n; i++) {
index = arr[i] / BUCKET_COUNT; // 简单的映射函数:元素值 / 桶数量
if (bucketElementsCount[index] < BUCKET_SIZE) {
buckets[index][bucketElementsCount[index]++] = arr[i];
} else {
// 处理桶溢出的情况(实际应用中需更健壮的处理)
printf("Bucket overflow!\\n");
}
}
// 2. 对每个非空桶进行排序(这里使用插入排序)
for (i = 0; i < BUCKET_COUNT; i++) {
if (bucketElementsCount[i] > 0) {
insertionSort(buckets[i], bucketElementsCount[i]);
}
}
// 3. 将排序后的所有桶中的元素依次放回原数组
index = 0;
for (i = 0; i < BUCKET_COUNT; i++) {
for (j = 0; j < bucketElementsCount[i]; j++) {
arr[index++] = buckets[i][j];
}
}
}
// 打印数组函数
void printArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\\n");
}
// 主函数测试
int main() {
int arr[] = {29, 25, 3, 49, 9, 37, 21, 43};
int n = sizeof(arr) / sizeof(arr[0]);
printf("排序前的数组: \\n");
printArray(arr, n);
bucketSort(arr, n);
printf("排序后的数组: \\n");
printArray(arr, n);
return 0;
}
1.5.2 代码关键点解释
- BUCKET_COUNT和 BUCKET_SIZE:根据数据范围预先定义桶的数量和每个桶的容量。
- buckets:一个二维数组,用于表示桶。
- bucketElementsCount:一个一维数组,记录每个桶中当前存放的元素数量,防止溢出。
- index = arr[i] / BUCKET_COUNT:这是映射函数,决定元素 arr[i]应该放入哪个桶。这个简单的映射函数适用于本例中0-99的范围。
- insertionSort:用于对每个非空桶内的元素进行排序。选择插入排序是因为它对于小规模数据效率不错且稳定。
1.6 与常见算法比较
| 算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 | 排序方式 | 核心思想与适用场景 |
|---|---|---|---|---|---|---|
| 桶排序 | O(n + k) | O(n²) | O(n + k) | 稳定 | 非比较 | 数据均匀分布、范围已知时效率极高。 |
| 快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 | 比较 | 通用性强,平均性能好,内部排序常用,但对数据分布敏感。 |
| 归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 | 比较 | 稳定,性能稳定,适合外部排序和大数据量,但需要O(n)额外空间。 |
| 堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 | 比较 | 原地排序,最坏情况也能保证O(n log n),但常数因子较大。 |
| 计数排序 | O(n + k) | O(n + k) | O(n + k) | 稳定 | 非比较 | 当整数范围k不大时极其高效,是桶排序的特例(每个桶一个值)。 |
从对比中可以看出,桶排序的主要优势在于其非比较的特性,在数据满足均匀分布和范围已知的前提条件下,性能可以非常出色。然而,其对数据分布的依赖性和额外的空间开销是其主要劣势。
1.7 总结
桶排序是一种基于分桶和映射的高效、稳定的非比较排序算法。其性能强烈依赖于输入数据的分布情况。当数据均匀分布在一个已知范围内时,它可以达到接近线性的时间复杂度,表现优异。
理解桶排序的关键在于掌握其分桶、桶内排序、合并的三步流程,以及映射函数的设计对性能的重要影响。虽然它在通用性上不如快速排序等算法,但在特定的适用场景下,桶排序是一个非常有价值的工具。
posted on 2025-11-06 17:18 weiwei2021 阅读(4) 评论(0) 收藏 举报
浙公网安备 33010602011771号