桶排序(Bucket Sort)是一种分布式排序算法,它将待排序的元素分配到若干个桶(Bucket)中,然后对每个桶中的元素进行排序,最后将所有桶中的元素按顺序合并。桶排序的核心思想是将数据分到有限数量的桶中,每个桶再分别排序(可以使用其他排序算法或递归地使用桶排序)。
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:
- 在额外空间充足的情况下,尽量增大桶的数量
- 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
算法步骤:
初始化桶:根据数据的范围和分布,创建若干个桶。
分配元素:遍历待排序的列表,将每个元素分配到对应的桶中。
排序每个桶:对每个桶中的元素进行排序(可以使用插入排序、快速排序等)。
合并桶:将所有桶中的元素按顺序合并,得到最终排序结果。
1. 什么时候最快
当输入的数据可以均匀的分配到每一个桶中。
2. 什么时候最慢
当输入的数据被分配到了同一个桶中。
3. 示意图
元素分布在桶中:
然后,元素在每个桶中排序:
假设有一个待排序的列表 [0.42, 0.32, 0.33, 0.52, 0.37, 0.47, 0.51],桶排序的过程如下:
初始化桶:
假设数据范围是 [0, 1),创建 10 个桶,每个桶的范围为 0.1。
桶 0:[0.0, 0.1)
桶 1:[0.1, 0.2)
...
桶 9:[0.9, 1.0)
分配元素:
遍历列表,将元素分配到对应的桶中:
0.42
→ 桶 40.32
→ 桶 30.33
→ 桶 30.52
→ 桶 50.37
→ 桶 30.47
→ 桶 40.51
→ 桶 5
分配后的桶:
桶 3:
[0.32, 0.33, 0.37]
桶 4:
[0.42, 0.47]
桶 5:
[0.52, 0.51]
排序每个桶:
对每个桶中的元素进行排序:
桶 3:
[0.32, 0.33, 0.37]
(已经有序)。桶 4:
[0.42, 0.47]
(已经有序)。桶 5:
[0.51, 0.52]
(排序后)。
合并桶:
按顺序合并所有桶中的元素:
桶 3:
[0.32, 0.33, 0.37]
桶 4:
[0.42, 0.47]
桶 5:
[0.51, 0.52]
合并后的列表:
[0.32, 0.33, 0.37, 0.42, 0.47, 0.51, 0.52]
。
实例
if len(arr) == 0:
return arr
# 找到最小值和最大值
min_val = min(arr)
max_val = max(arr)
# 初始化桶
bucket_count = (max_val - min_val) // bucket_size + 1
buckets = [[] for _ in range(bucket_count)]
# 分配元素到桶中
for num in arr:
index = int((num - min_val) // bucket_size)
buckets[index].append(num)
# 对每个桶中的元素进行排序
sorted_arr = []
for bucket in buckets:
sorted_arr.extend(sorted(bucket)) # 使用内置排序函数
return sorted_arr
# 示例
arr = [0.42, 0.32, 0.33, 0.52, 0.37, 0.47, 0.51]
sorted_arr = bucket_sort(arr)
print(sorted_arr) # 输出: [0.32, 0.33, 0.37, 0.42, 0.47, 0.51, 0.52]
时间复杂度
-
分配元素:O(n),遍历列表一次。
-
排序每个桶:假设每个桶中的元素数量为 m,则排序一个桶的时间复杂度为 O(m log m)。如果桶的数量为 k,则总时间复杂度为 O(k * m log m)。
-
合并桶:O(n),遍历所有桶一次。
-
总时间复杂度:O(n + k * m log m),其中 n 是列表长度,k 是桶的数量,m 是每个桶的平均元素数量。
空间复杂度
-
O(n + k),需要额外的存储空间来存放桶和排序后的结果。
优缺点
-
优点:
-
当数据分布均匀时,性能优异。
-
适合外部排序(如对磁盘文件进行排序)。
-
-
缺点:
-
当数据分布不均匀时,某些桶中的元素数量过多,导致性能下降。
-
需要额外的存储空间。
-
适用场景
-
数据分布均匀的场景。
-
数据范围已知且有限的场景。
-
适合外部排序(如对磁盘文件进行排序)。
代码实现
JavaScript
实例
if (arr.length === 0) {
return arr;
}
var i;
var minValue = arr[0];
var maxValue = arr[0];
for (i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i]; // 输入数据的最小值
} else if (arr[i] > maxValue) {
maxValue = arr[i]; // 输入数据的最大值
}
}
//桶的初始化
var DEFAULT_BUCKET_SIZE = 5; // 设置桶的默认数量为5
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
var buckets = new Array(bucketCount);
for (i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
//利用映射函数将数据分配到各个桶中
for (i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
arr.length = 0;
for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]); // 对每个桶进行排序,这里使用了插入排序
for (var j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
return arr;
}
Java
实例
private static final InsertSort insertSort = new InsertSort();
@Override
public int[] sort(int[] sourceArray) throws Exception {
// 对 arr 进行拷贝,不改变参数内容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
return bucketSort(arr, 5);
}
private int[] bucketSort(int[] arr, int bucketSize) throws Exception {
if (arr.length == 0) {
return arr;
}
int minValue = arr[0];
int maxValue = arr[0];
for (int value : arr) {
if (value < minValue) {
minValue = value;
} else if (value > maxValue) {
maxValue = value;
}
}
int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1;
int[][] buckets = new int[bucketCount][0];
// 利用映射函数将数据分配到各个桶中
for (int i = 0; i < arr.length; i++) {
int index = (int) Math.floor((arr[i] - minValue) / bucketSize);
buckets[index] = arrAppend(buckets[index], arr[i]);
}
int arrIndex = 0;
for (int[] bucket : buckets) {
if (bucket.length <= 0) {
continue;
}
// 对每个桶进行排序,这里使用了插入排序
bucket = insertSort.sort(bucket);
for (int value : bucket) {
arr[arrIndex++] = value;
}
}
return arr;
}
/**
* 自动扩容,并保存数据
*
* @param arr
* @param value
*/
private int[] arrAppend(int[] arr, int value) {
arr = Arrays.copyOf(arr, arr.length + 1);
arr[arr.length - 1] = value;
return arr;
}
}
PHP
实例
{
if (count($arr) === 0) {
return $arr;
}
$minValue = $arr[0];
$maxValue = $arr[0];
for ($i = 1; $i < count($arr); $i++) {
if ($arr[$i] < $minValue) {
$minValue = $arr[$i];
} else if ($arr[$i] > $maxValue) {
$maxValue = $arr[$i];
}
}
$bucketCount = floor(($maxValue - $minValue) / $bucketSize) + 1;
$buckets = array();
for ($i = 0; $i < $bucketCount; $i++) {
$buckets[$i] = [];
}
for ($i = 0; $i < count($arr); $i++) {
$buckets[floor(($arr[$i] - $minValue) / $bucketSize)][] = $arr[$i];
}
$arr = array();
for ($i = 0; $i < count($buckets); $i++) {
$bucketTmp = $buckets[$i];
sort($bucketTmp);
for ($j = 0; $j < count($bucketTmp); $j++) {
$arr[] = $bucketTmp[$j];
}
}
return $arr;
}
C++
实例
#include<iostream>
#include<vector>
using namespace std;
const int BUCKET_NUM = 10;
struct ListNode{
explicit ListNode(int i=0):mData(i),mNext(NULL){}
ListNode* mNext;
int mData;
};
ListNode* insert(ListNode* head,int val){
ListNode dummyNode;
ListNode *newNode = new ListNode(val);
ListNode *pre,*curr;
dummyNode.mNext = head;
pre = &dummyNode;
curr = head;
while(NULL!=curr && curr->mData<=val){
pre = curr;
curr = curr->mNext;
}
newNode->mNext = curr;
pre->mNext = newNode;
return dummyNode.mNext;
}
ListNode* Merge(ListNode *head1,ListNode *head2){
ListNode dummyNode;
ListNode *dummy = &dummyNode;
while(NULL!=head1 && NULL!=head2){
if(head1->mData <= head2->mData){
dummy->mNext = head1;
head1 = head1->mNext;
}else{
dummy->mNext = head2;
head2 = head2->mNext;
}
dummy = dummy->mNext;
}
if(NULL!=head1) dummy->mNext = head1;
if(NULL!=head2) dummy->mNext = head2;
return dummyNode.mNext;
}
void BucketSort(int n,int arr[]){
vector<ListNode*> buckets(BUCKET_NUM,(ListNode*)(0));
for(int i=0;i<n;++i){
int index = arr[i]/BUCKET_NUM;
ListNode *head = buckets.at(index);
buckets.at(index) = insert(head,arr[i]);
}
ListNode *head = buckets.at(0);
for(int i=1;i<BUCKET_NUM;++i){
head = Merge(head,buckets.at(i));
}
for(int i=0;i<n;++i){
arr[i] = head->mData;
head = head->mNext;
}
}
参考地址:
https://github.com/hustcc/JS-Sorting-Algorithm/blob/master/9.bucketSort.md
https://zh.wikipedia.org/wiki/%E6%A1%B6%E6%8E%92%E5%BA%8F
zihe.zhu
zih***[email protected]
参考地址
zihe.zhu
zih***[email protected]
参考地址
艾孜尔江
bju***[email protected]
又没把C#的写进来,我来写掉吧,代码如下:
艾孜尔江
bju***[email protected]
往事訴與風
fig***[email protected]
以下是根據參考答案思路,整理出 C# 版本的桶排序算法:
往事訴與風
fig***[email protected]
好的艰苦大师傅
che***[email protected]
C语言版本。注:写于2024.4.2日,作者:Connor
好的艰苦大师傅
che***[email protected]