建议数据范围:0-100 (避免极端值影响分桶效果)。
桶排序(Bucket Sort)是一种基于分治思想的分布式排序算法。分治思想即将大问题分解为若干个规模较小、相互独立、且与原问题形式相同的子问题,然后递归地解这些子问题,最后将各子问题的解合并,从而得到原问题的解。桶排序正是应用了这一思想,它将待排序的数据分到有限数量的桶里,然后对每个桶单独进行排序(可以看作子问题),最后依次把各个桶中的数据合并起来(合并解),得到完全排序的序列。
桶排序的核心在于 如何设定桶的范围 以及 如何将元素映射到桶。一般步骤如下:
注意: 实际实现需要处理边界情况,例如空数组、只有一个元素的数组、或所有元素都相同的情况(如示例代码所示)。
桶排序最适用于 输入数据均匀分布 在一个已知范围内的情况。如果数据分布极不均匀(例如,所有数据都落入同一个桶),桶排序的性能会退化到桶内排序算法的性能,失去其优势。
假设有 n 个元素,k 个桶:
平均情况(数据均匀分布): 总时间复杂度接近 O(n + k)。当 k 与 n 接近(k ≈ n)且桶内排序接近线性时(如小桶用插入排序),理论上可达 O(n)。
最坏情况(所有元素在一个桶): 复杂度退化为桶内排序算法的复杂度,通常是 O(n log n)(如果使用高效排序)或 O(n²)(如果使用简单排序)。
需要 O(n + k) 的额外空间来存储 k 个桶以及桶内的 n 个元素。
#include <iostream> // 用于输入输出 (For input/output)
#include <vector> // 用于动态数组 (For dynamic arrays)
#include <algorithm> // 包含 sort, min, max 等算法 (Includes sort, min, max algorithms)
#include <cmath> // 用于数学函数,例如 abs (For math functions like abs)
#include <limits> // 用于获取浮点数极值,如 epsilon (For numeric limits, e.g., epsilon)
using namespace std;
// 桶排序函数 (处理浮点数,可适配整数)
// arr: 待排序的浮点数向量 (通过引用传递以修改原数组)
// bucketCount: 要使用的桶的数量
void bucketSort(vector& arr, int bucketCount) {
int n = arr.size();
// 处理边界情况:空数组或只有一个元素,无需排序
if (n <= 1) return;
// 1. 找到数组中的最大值和最小值
float minValue = arr[0];
float maxValue = arr[0];
for (int i = 1; i < n; ++i) {
minValue = min(minValue, arr[i]); // 使用 min
maxValue = max(maxValue, arr[i]); // 使用 max
}
// 如果所有元素都相同,则无需排序 (另一个边界情况)
if (minValue == maxValue) return;
// 确保桶数量至少为1
// Ensure bucketCount is at least 1
if (bucketCount <= 0) bucketCount = 1;
// 2. 计算桶的范围 (确保至少为很小的正数,防止除零或区间为0)
// 注意:特殊处理最大值,将其放入最后一个桶是更稳健的做法
float range = (maxValue - minValue) / bucketCount;
// 处理 range 极小或为 0 的情况 (例如所有值非常接近)
const float epsilon = numeric_limits::epsilon(); // 获取机器 epsilon
if (range < epsilon) range = epsilon; // 使用机器 epsilon 作为最小范围
// 3. 创建桶(使用二维向量,每个桶是一个 vector)
vector> buckets(bucketCount);
// 4. 将元素分配到对应的桶中
for (int i = 0; i < n; ++i) {
// 计算元素应该放入哪个桶的索引
int bucketIndex;
// 使用 epsilon 进行浮点数比较,更稳健,处理最大值
if (abs(arr[i] - maxValue) < epsilon) {
bucketIndex = bucketCount - 1; // 显式将最大值(或非常接近的值)放入最后一个桶
} else if (abs(arr[i] - minValue) < epsilon && bucketCount > 1) {
// 最小值(或接近的)放入第一个桶(如果多于一个桶)
bucketIndex = 0;
} else {
// 通过 (当前值 - 最小值) / 区间大小 来确定相对位置
bucketIndex = static_cast((arr[i] - minValue) / range);
}
// 边界检查,防止计算误差导致越界 (钳位到 [0, bucketCount-1])
bucketIndex = max(0, min(bucketIndex, bucketCount - 1)); // 使用 max, min
buckets[bucketIndex].push_back(arr[i]); // 将元素添加到桶中 / Add element to the bucket
}
// 5. 对每个非空桶内的元素进行排序
for (int i = 0; i < bucketCount; ++i) {
if (!buckets[i].empty()) {
// 这里使用 C++ 标准库的排序算法 (通常是 IntroSort, 平均 O(N log N))
// 对于非常小的桶,插入排序可能因常数因子小而更快 (但最坏 O(N^2))
sort(buckets[i].begin(), buckets[i].end()); // 使用 sort
// 示例:如果需要对小桶使用插入排序 (可选优化)
// const int INSERTION_SORT_THRESHOLD = 32;
// if (buckets[i].size() < INSERTION_SORT_THRESHOLD) {
// insertionSort(buckets[i]); // 假设有 insertionSort 函数 / Assuming an insertionSort function exists
// } else {
// sort(buckets[i].begin(), buckets[i].end());
// }
}
}
// 6. 将所有桶中的元素按顺序合并回原数组
int currentIndex = 0;
for (int i = 0; i < bucketCount; ++i) {
for (float val : buckets[i]) {
arr[currentIndex++] = val;
}
}
}
// 示例主函数 (Example main function)
int main() {
vector data = {0.42, 10.32, 0.33, 0.52, 10.37, 0.47, 0.5, 1.7, 0.12, 8.98, 0.66};
int numBuckets = 5; // 指定桶数
cout << "原数组: ";
for (float val : data) cout << val << " ";
cout << endl;
bucketSort(data, numBuckets); // 调用桶排序
cout << "Sorted array (" << numBuckets << " buckets): ";
for (float val : data) cout << val << " ";
cout << endl;
return 0;
}
注意: 上述代码为了教学和简洁性使用了 float
并依赖 sort
进行桶内排序。在实际生产环境中,对于非常小的桶,自定义的插入排序可能更高效。强烈推荐在项目中使用 前缀代替
using namespace std;
以避免命名空间污染。
// 使用类似C的伪代码风格
算法: BucketSort(A)
输入: 待排序数组 A (包含 n 个元素)
输出: 排序后的数组 A
参数: k - 桶的数量 (可选, 可根据数据特性设定, k > 0)
函数 BucketSort(A, k):
n ← A 的长度
如果 n ≤ 1 则
返回 A // 数组为空或只有一个元素,无需排序
结束如果
// 1. 找到最大值 max 和最小值 min
minVal ← A[0]
maxVal ← A[0]
对于 i 从 1 到 n-1 执行
minVal ← min(minVal, A[i])
maxVal ← max(maxVal, A[i])
结束对于
如果 minVal = maxVal 则
返回 A // 所有元素相同
结束如果
// 确保 k 合法
如果 k ≤ 0 则 k ← 默认值 (例如 n 或 10)
// 2. 初始化 k 个空桶
创建 k 个桶 B[0...k-1]
// 3. 计算每个桶的预期范围或映射规则
range ← (maxVal - minVal) / k
// 处理range为0或极小的情况 (使用epsilon)
如果 range ≈ 0 则 range = 一个很小的正数 (epsilon)
// 4. 将 A 中的元素分配到各个桶 B 中
对于 i 从 0 到 n-1 执行
// 计算桶索引,注意处理最大值和浮点精度
如果 A[i] ≈ maxVal (使用容差比较) 则
index ← k - 1 // 确保最大值放入最后一个桶
否则 如果 A[i] ≈ minVal (使用容差比较 且 k > 1) 则
index ← 0 // 确保最小值放入第一个桶 (若多于一个桶)
否则
index ← floor((A[i] - minVal) / range)
结束如果
// 边界钳位,确保索引在 [0, k-1]
index ← max(0, min(index, k - 1))
将 A[i] 添加到桶 B[index] 中
结束对于
// 5. 对每个桶 B[i] 内部进行排序
对于 i 从 0 到 k-1 执行
如果 桶 B[i] 不为空 则
// 使用其他排序算法 (如插入排序, 归并排序等) 对 B[i] 进行排序
Sort(B[i]) // Sort 是指代任何合适的排序算法
结束如果
结束对于
// 6. 按顺序合并所有桶中的元素回数组 A
currentIndex ← 0
对于 i 从 0 到 k-1 执行
对于 B[i] 中的每个元素 e 执行
A[currentIndex] ← e
currentIndex ← currentIndex + 1
结束对于
结束对于
返回 A // 返回排序后的 A
结束函数
算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 | 适用场景 |
---|---|---|---|---|---|
桶排序 (Bucket Sort) | O(n+k) (理想)*** | O(n log n) 或 O(n²) * | O(n+k) | 依赖桶内排序 | 数据均匀分布,范围已知 |
快速排序 (Quick Sort) | O(n log n) | O(n²) | O(log n) 或 O(n) | 不稳定 | 通用,大多数情况高效 |
归并排序 (Merge Sort) | O(n log n) | O(n log n) | O(n) | 稳定 | 需要稳定性,大数据量 |
插入排序 (Insertion Sort) | O(n²) | O(n²) | O(1) | 稳定 | 小数据量,近乎有序 |
计数排序 (Counting Sort) | O(n+k) ** | O(n+k) ** | O(k) ** | 稳定 | 整数,范围 k 不太大 |
* 桶排序最坏情况取决于桶内排序算法以及数据分布。如果所有元素落入一个桶,复杂度等于该桶内排序算法的复杂度(若用高效算法如 `sort`,则为 O(n log n);若用简单算法如插入排序,则为 O(n²))。
** 计数排序中 k 是数据范围的最大值与最小值的差(或最大值本身,如果从0开始计算)。
*** 桶排序的 O(n+k) 复杂度是理想情况,前提是数据均匀分布使得每个桶元素数量接近 n/k,且桶内排序时间接近线性(例如对小桶使用插入排序)。