【C语言】【十大排序算法】桶排序
·
原理
将元素分布到多个桶中,每个桶内部使用其他排序算法(如插入排序)进行排序,最后将所有桶中的元素合并成一个有序列表。桶排序适用于数据分布均匀的情况,特别适合处理浮点数和小范围整数的排序。桶划分越多,每个桶里面的数据越少,排序时间越短,但相应地占用空间越大。
时间复杂度
最好情况:O(n),当所有元素均匀分布在各个桶中时。
平均情况:O(n),当元素分布较为均匀时。
最坏情况:O(n^2),当所有元素都落在同一个桶中时。
适用场景
桶排序适用于数据范围明确且分布较为均匀的情况。当输入数据均匀分布在桶中时,排序效率非常高,时间复杂度接近O(n),其中n是数组长度。然而,如果数据分布极不均匀,桶排序的效率会大大降低,最坏情况下的时间复杂度接近O(n^2),特别是当所有元素都落在同一个桶中时。
优缺点
优点:
当数据分布均匀时,排序效率高。
适用于大量数据的排序,尤其是浮点数的排序。
缺点:
数据分布不均匀时,效率低下。
需要额外的空间来创建和存储桶。
实现步骤:
1.设置一个定量的数组当作空桶;
2.遍历输入数据,并且把数据一个一个放到对应的桶里去;
3.对每个不是空的桶进行排序;
4.从不是空的桶里把排好序的数据拼接起来。
C语言案例
#include <stdio.h>
#include <stdlib.h>
/** ↓ ↓ ↓ 插入排序函数(辅助函数) ↓ ↓ ↓ **/
void insertionSort(float arr[], const int n) {
for (int i = 1; i < n; i++) {
float key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
/** ↑ ↑ ↑ 插入排序函数(辅助函数) ↑ ↑ ↑ **/
// 桶排序函数
/*
1. 确定最大值和最小值:遍历数组,找到最大值和最小值。
2. 创建桶:根据最大值和最小值计算桶的数量和每个桶的大小,创建相应数量的桶。
3. 分配数据到桶:根据每个元素的值,将其分配到对应的桶中。
4. 排序每个桶:对每个桶中的数据使用插入排序进行排序。
5. 合并桶:将所有桶中的数据按顺序合并回原数组。
6. 释放内存:释放分配的内存。
*/
void bucketSort(float arr[], const int numsSize) {
float max_value = arr[0];
float min_value = arr[0];
for (int i = 1; i < numsSize; i++) {
if (arr[i] > max_value) max_value = arr[i]; // 找到原数组最大值
if (arr[i] < min_value) min_value = arr[i]; // 找到原数组最小值
}
const int bucket_count = numsSize; // 桶的数量
const float bucketInterval = (max_value - min_value) / bucket_count; // 每个桶存储元素的区间,比如每个桶存储区间为0.9,即每个桶都是以0.9为增量进行存储的。
// 为桶申请空间
float **buckets = (float **) malloc(bucket_count * sizeof(float *));
for (int i = 0; i < bucket_count; i++) {
buckets[i] = (float *) malloc(numsSize * sizeof(float));
}
// calloc用于动态分配内存,calloc会初始化分配的内存为0。这里分配bucket_count个元素,每个元素的大小为sizeof(int)
// calloc 会分配足够的内存来容纳 num 个大小为 size 的元素,并将分配的内存初始化为零。
// 具体来说,calloc 会调用 malloc 来分配内存,然后使用 memset 将分配的内存区域设置为零。
// 此处目的是为每个桶分配一个计数器数组,用于记录每个桶中包含的元素数量。
// 具体来说,bucket_sizes数组将存储每个桶中元素的数量,以便在后续的排序和合并过程中使用。
int *bucketEleCountSizes = (int *) calloc(bucket_count, sizeof(int));
// 分配数据到桶
for (int i = 0; i < numsSize; i++) {
// 确定当前元素arr[i]应该放入那个桶中的索引。
int index = (arr[i] - min_value) / bucketInterval;
if (index == bucket_count) index--; // 处理等于max_value的情况(边界场景处理)
// 数组bucketEleCountSizes被初始化为全0,数组中元素数量和桶的数量相等,bucketEleCountSizes[index]初始为0,
// 也就是说,每个桶中将会从头到尾按顺序进行分配元素。
// 这样做得好处是,如果有相同值的元素,也会被按顺序放入桶中。如果理解不了,这就是固定写法,记住就行了。
buckets[index][bucketEleCountSizes[index]] = arr[i];
bucketEleCountSizes[index]++; // 将第index个桶的元素数量+1,bucketEleCountSizes数组的作用就是记录每个桶的元素数量
}
// 排序每个桶
for (int i = 0; i < bucket_count; i++) {
insertionSort(buckets[i], bucketEleCountSizes[i]);
}
// 合并桶
int index = 0;
for (int i = 0; i < bucket_count; i++) {
for (int j = 0; j < bucketEleCountSizes[i]; j++) {
arr[index++] = buckets[i][j];
}
}
// 释放内存
for (int i = 0; i < bucket_count; i++) {
free(buckets[i]);
}
free(buckets); // buckets是二维数组,这里没有多重释放
free(bucketEleCountSizes);
}
int main() {
float arr[] = {0.78, 0.17, 0.39, 0.21, 0.26, 0.00, 0.21, 0.72, 0.94, 0.21, 0.12, 0.23, 0.68};
const int numsSize = sizeof(arr) / sizeof(float);
printf("排序前:\n");
for (int i = 0; i < numsSize; i++) {
printf("%.2f ", arr[i]);
}
printf("\n");
bucketSort(arr, numsSize);
printf("排序后:\n");
for (int i = 0; i < numsSize; i++) {
printf("%.2f ", arr[i]);
}
printf("\n");
return 0;
}
代码运行结果
排序前:
0.78 0.17 0.39 0.21 0.26 0.00 0.21 0.72 0.94 0.21 0.12 0.23 0.68
排序后:
0.00 0.12 0.17 0.21 0.21 0.21 0.23 0.26 0.39 0.68 0.72 0.78 0.94
更多推荐


所有评论(0)