学习目标:
掌握简单的排序算法
学习内容:
1.桶排序
一、桶排序
桶排序 (Bucket sort)或所谓的箱排序,是一种分块的排序算法,工作的原理是将数组分到有限数量的桶里,每个桶的大小都相等。每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)
把待排序序列(数组)中的数据根据函数映射方法分配到若干个桶中,在分别对各个桶进行排序,最后依次按顺序取出桶中的数据。
适用于数据分配均匀,数据比较大,相对集中的情况。
在我看来 桶排序其实就是用到哈希表的方法,首先需要排序的数放入一个数组当中,然后再重新定义一个哈希数组,哈希数组的长度要超过需要排序的最大值,不然就会出现错误。对需要排序的数组进行遍历,遇到某个数是哈希数组下标则在里面加一,例如定义一个b[10],遍历,然后haxi[b[i]]++,最后对哈希数组输出,如果储存的数大于零,则对下标进行输出。
当然,这是一种最简单的桶排序的方法,有桶排序基本原理在里面,但是这种方法存在并没有便利多少,而接下来则是桶排序的进阶方法
桶排序进阶方法(真正意义上的桶排序)
这种方法所需要的“桶“不需要几个,这也说明数组的长度不会太大。
核心思想:就是将大问题化小(分治思想)
(1)例如:数组:
(2)观察知:数组的元素分布在(0~50)之间,我们可以将其分隔成五个区间分辨是[0~9],[19~19],[20~29],[30~39],[40~49];(桶的个数根据题意自定,只需确定好每个桶的存储范围就好;并非桶越多越好,也并非越少越好总之适当就好);
(3)这五个区间看做五个桶;分别存放符合范围的数字;
(4)将这五个区间分别排序,再输出;
注:根据所给的数的范围的差异,桶的个数也会存在差异。
例如:
生成了20个3位的随机数(0~999),按照百位的数字分别插入到不同的链表中。
函数映射: f(x)=x/100 ,而生成的桶数也来到了九个。
桶排序误区
1、如果数据分布不均,大量数据集中在少量桶里,桶排序就没有效果。
2、桶排序要时间就省不了空间,要空间就省不了时间,意义不大。
首先桶排序排序的内容是均匀分布的一串数字,不存在数据分布不均的问题。其次桶排序可以在时间和空间之间找一个点,使其满足两者。
个人理解:
桶排序运用了分布的思想,将一串很大的数字分成若干个不同的部分,然后在每个部分中进行排序。
二、代码实现
普通版:
#include <stdio.h>
#include <stdlib.h>
// 桶排序函数
void bucketSort(int arr[], int n) {
// 创建桶数组
int maxVal = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] > maxVal) {
maxVal = arr[i];
}
}
int bucket[maxVal + 1];
for (int i = 0; i <= maxVal; i++) {
bucket[i] = 0;
}
// 将元素放入桶中
for (int i = 0; i < n; i++) {
bucket[arr[i]]++;
}
// 从桶中取出元素并排序
int index = 0;
for (int i = 0; i <= maxVal; i++) {
while (bucket[i] > 0) {
arr[index++] = i;
bucket[i]--;
}
}
}
// 测试桶排序
int main() {
int arr[] = {5, 2, 8, 4, 9, 1, 3, 7, 6};
int n = sizeof(arr) / sizeof(arr[0]);
printf("原始数组:");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
bucketSort(arr, n);
printf("排序后数组:");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
进阶版:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int num; //数据域
struct node *next; //指针域
}KeyNode;
void bucket_sort(int a[],int size,int bucket_size) {
int i,j; //数组,数组长度,桶的大小
//定义动态的指针数组
KeyNode **bucket_num = (KeyNode **)malloc(bucket_size * sizeof(KeyNode*));
for(i = 0;i < bucket_size;i++)
{
bucket_num[i] = (KeyNode*)malloc(sizeof(KeyNode));//为每个链表定义头结点
bucket_num[i]->num = 0;
bucket_num[i]->next = NULL; //指针变量初始化为空
}
for(j = 0;j < size;j++) //准备插入
{
KeyNode *node = (KeyNode *)malloc(sizeof(KeyNode));//定义一个节点
node->num = a[j]; //数据域存数据
node->next = NULL; //指向空
int index = a[j]/100; //映射函数 计算桶号
KeyNode *p = bucket_num[index];//p指向链表的头
//链表结构的插入排序
while(p->next != NULL && p->next->num <= node->num)
{
p = p->next; //1.链表为空,p->next==NULL,进入不了循环
} //2.链表不为空,因为链表从无开始按顺序插入,数据为有序的,
//可以找到 前一个节点 <= node <=后一个节点
//节点插入链表
node->next = p->next;
p->next = node;
(bucket_num[index]->num)++; //记录一下该链表中有几个有效节点
}
//打印结果
KeyNode * k = NULL; //定义一个空的结构体指针用于储存输出结果
for(i = 0;i < bucket_size;i++)
{
//for(k = bucket_num[i]->next;k!=NULL;k=k->next)//通过最后一个指针指向空
k = bucket_num[i]->next;
for(int m=0;m<bucket_num[i]->num;m++) //通过头指针记录节点数
{
printf("%d ",k->num);
k=k->next;
}
printf("\n");
}
int main()
{
int a[20];
for(int i=0;i<20;i++)
{
a[i]=rand()%1000; //给数组赋随机数
printf("%d ",a[i]);
}
puts("");
puts("");
//int a[]={491,381,615,917,716,13,217,419,19,138,61,917,176,113,27,419,419,38,615,917,16,113,217,419};
int size = sizeof(a)/sizeof(int); //计算数组长度
bucket_sort(a,size,10);//数组名,数组长度,桶的个数
}