桶排序
1. 算法思想
桶排序(Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶里,每个桶再分别排序(大部分是在分桶时,即插入时就排序了)。
个人理解,适合数据比较集中排序,桶的数量适当设置。
2. 实现原理
桶排序以下列程序进行:
- 设置一个定量的数组当作空桶子。
- 寻访序列,并且把项目一个一个放到对应的桶子去。
- 对每个不是空的桶子进行排序。
- 从不是空的桶子里把项目再放回原来的序列中。
3. 动态演示
(1)数据分桶
(2)桶内数据排序(大部分是在分桶时,即插入时就排序了)
(3)然后连接就好了
4. 完整代码
主要函数
插入函数:void insert(BN* list, int value)
排序函数:void bucket_sort(int* array, int size, int num)
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h> // rand() srand()
#include <time.h> // time()
typedef struct BucketNode
{
int data;
struct BucketNode* next;
} BN;
void displayL(BN* L); // 输出链表
void display(int* array, int size); // 输出数组
int check(int* array, int size); // 检查函数
/***************************************************************************
* @date 2020/12/03
* @brief 合并链表
* @param head 头指针
* @param list 顺序数据链表
***************************************************************************/
BN* merge(BN* head, BN* list)
{
BN* last = head;
last->next = list->next;
while (last->next) {
last = last->next;
}
return last;
}
/***************************************************************************
* @date 2020/12/03
* @brief 顺序插入节点
* @param list 代表第几个桶的链表
* @param value 数据
***************************************************************************/
void insert(BN* list, int value)
{
BN* prev = list;
BN* curr = list->next;
BN* node = (BN*)malloc(sizeof(BN));
node->data = value;
node->next = NULL;
if (curr == NULL) {
prev->next = node;
} else {
while (curr != NULL && curr->data < value) {
prev = curr;
curr = curr->next;
}
prev->next = node;
node->next = curr;
}
}
/***************************************************************************
* @date 2020/12/03
* @brief 桶排序主程序
* @param array 数组
* @param size 数组大小
* @param num 几个桶
***************************************************************************/
void bucket_sort(int* array, int size, int num)
{
// 申请内存,二级指针,初始化,可以理解头指针没数据,从下一个开始存数数据
BN** buckets = (BN**)malloc(sizeof(BN*) * num);
for (int i = 0; i < num; i++) {
*(buckets + i) = (BN*)malloc(sizeof(BN));
(*(buckets + i))->next = NULL;
}
// 1. 找到最大值和最小值求间隔(桶的大小)
int max = array[0];
int min = array[0];
for (int i = 0; i < size; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
int space = ((max - min) / num) + 1;
// 2. 一个一个分桶排序
for (int i = 0; i < size; i++) {
int n = (array[i] - min) / space;
insert(*(buckets + n), array[i]);
}
for (int i = 0; i < num; i++) {
printf("第 %d 个桶数据: ", i);
displayL((*(buckets + i))->next);
}
// // 3. 合并链表
// BN* head = (BN*)malloc(sizeof(BN));
// head->next = NULL;
// BN* last = merge(head, *(buckets + 0));
// for (int i = 1; i < num; i++) {
// if ((*(buckets + i))->next) {
// last = merge(last, *(buckets + i));
// }
// }
// head = head->next;
// // 4. 把链表值返回数组
// for (int i = 0; i < size; i++) {
// array[i] = head->data;
// head = head->next;
// }
// 3+4. 当然也可以不合并链表,直接把数据返回数组
int index = 0;
for (int i = 0; i < num; i++) {
if ((*(buckets + i))->next != NULL) {
BN* temp = (*(buckets + i))->next;
while (temp != NULL) {
array[index++] = temp->data;
temp = temp->next;
}
}
}
}
int main()
{
// 测试用例
// int array[] = {49, 38, 65, 97, 76, 13, 27, 49, 10};
// int array_size = sizeof(array) / sizeof(array[0]);
// int bucket_num = 5;
// printf("%d \n", array_size);
// printf("排序前数组:");
// display(array, array_size);
// bucket_sort(array, array_size, bucket_num);
// printf("排序后数组:");
// display(array, array_size);
// 随机测试
int bucket_num = 5; // 桶的个数
int array_num = 20; // 数组数量
int array_size = 20; // 数组大小
int array[array_size]; // 数组初始化
srand((unsigned int)time(NULL)); // 随机数种子,保证每次不一样
for (int i = 0; i < array_num; i++) {
for (int j = 0; j < array_size; j++) {
array[j] = rand() % 1000; // 随机生成数大小 0~999
}
printf("原来的数组:");
display(array, array_size);
bucket_sort(array, array_size, bucket_num);
printf("排序后数组:");
display(array, array_size);
// 检测排序结果
if (check(array, array_size) != 0) {
exit(-1);
}
printf("\n");
}
return 0;
}
/***************************************************************************
* @date 2020/12/03
* @brief 输出线性表
* @param L 首节点
***************************************************************************/
void displayL(BN* L)
{
BN* p = L; // p 指向首结点
while (p != NULL) { // 不为空,依次遍历
printf("%d ", p->data); // 打印
p = p->next; // p 移向下一个节点
}
printf("\n");
}
/***************************************************************************
* @date 2020/12/03
* @brief 输出数组
* @param array 数组
* @param size 数组大小
***************************************************************************/
void display(int* array, int size)
{
for (int i = 0; i < size; i++) {
printf("%d ", array[i]);
}
printf("\n");
}
/**
* @brief 检查函数,从小到大
*
* @param array 数组首指针
* @param size 数组大小
*/
int check(int* array, int size)
{
for (int i = 0; i < size - 1; i++) {
if (array[i] > array[i + 1]) {
printf("sort array fail...\n");
return -1;
}
}
printf("sort array success...\n");
return 0;
}
5. 结果展示
6. 算法分析
时间复杂度:
- 最好: O ( n ) O(n) O(n)
- 最坏: O ( n 2 ) O(n^{2}) O(n2)
- 平均: O ( n + k ) O(n+k) O(n+k)
空间复杂度: O ( n ∗ k ) O(n*k) O(n∗k)
稳定性:稳定(也有说根据桶内排序决定稳定性)
7. 参考资料
[1] 【算法】排序算法之桶排序——使用动态图
[2] Bucket sort——维基百科,仿写 C 代码
[3] 动态演示 ——数据结构动态演示网站