思路:
假设待排序的数据集,平均的分散在各个区间里(最理想的状态是每个区间有且只有一个元素),
那么我只要将每个数据落入到响应的区间里,然后从小到大依次从区间中取出数据,即是已排序好的结果
这里的区间,其实就是桶的概念
空间代价:
取决于放入桶中的元素的数据结构,我这里采用的是一个指向下一个元素的指针以及当前int元素,
基本可以人物空间代价为O(n)
时间代价:
取决于每个桶中所有元素的排序算法的时间效率,教科书上TMD解释的非常复杂,看不懂,但一个基本原则就是,如果已知待排序的最大直和最小值,并且其均匀分布,则桶排序的效率还是比较乐观的,回头等这个系统全部写完了以后,会对这些方案进行一个全面的性能测试
算法实现:
#ifndef __p1__BucketSort__
#define __p1__BucketSort__
#include <stdio.h>
#include <string.h>
#include <cstddef>
#include <list>
#include <math.h>
//?
//桶排序
//核心思想:
//假设待排序的数据集,平均的分散在各个区间里(最理想的状态是每个区间有且只有一个元素),
//那么我只要将每个数据落入到响应的区间里,然后从小到大依次从区间中取出数据,即是已排序好的结果
//这里的区间,其实就是桶的概念
//桶元素
typedef struct bucket_item {
int value;//值
struct bucket_item * next;
}BucketItem;
//桶
typedef struct bucket {
int hasValue;// 1 表示当前位置有值
BucketItem * head;
}Bucket;
class BucketSort {
int _bucketSize = 100; //定义100个桶
int _bucketRangeStep = 0;
int needFreeItemCount = 0;//需要被free的元素的个数
BucketItem * itemNeedToFree = NULL; //待free的元素空间
public:
//计算给定数组的最大值和最小值
void calculateMaxAndMinValue(int * data,int size,int * minValue,int * maxValue){
if(size ==1){
*(maxValue) = *data;
*(minValue) = *data;
return;
}
//size = 3
int topIndex = (size % 2 == 0) ? size - 1 : size - 2 ,i = 0;
for(;i <= topIndex - 1;){
if (i == 0) {
if (*(data + i) > *(data + i + 1)) {
*maxValue = *(data + i);
*minValue = *(data + i + 1);
}else{
*maxValue = *(data + i + 1);
*minValue = *(data + i);
}
}else{
//每两个元素只比较3次,较之每个元素比较两次(两个元素比较4次),要少一次比较
if (*(data + i) > *(data + i + 1)) {
if(*(data + i) > *maxValue){
*maxValue = *(data + i);
}
if(*(data + i + 1) < *minValue){
*minValue = *(data + i + 1);
}
}else{
if(*(data + i + 1) > *maxValue){
*maxValue = *(data + i + 1);
}
if(*(data + i ) < *minValue){
*minValue = *(data + i );
}
}
}
//跳过2个元素
i = i + 2;
}
//奇数,还剩最后一个元素没比较,其只需要比较一次
if(size % 2 != 0){
if(*(data + size - 1) > *maxValue){
*maxValue = *(data + size - 1);
}else if(*(data + size - 1) < *minValue){
*minValue = *(data + size - 1);
}
}
}
//初始化桶容器
Bucket * initBucketContainer(int minValue,int maxValue){
_bucketRangeStep = floorf((maxValue - minValue) / _bucketSize);
Bucket * buketContainer = new Bucket [_bucketSize];
memset(buketContainer,'\0',sizeof(Bucket)*_bucketSize);
return buketContainer;
}
//计算元素应该落在第几个桶中
//规则是,按照最大和最小值,将元素均分为_bucketSize个分区
int calculateBucketPosition(int data,int minValue,int maxValue){
int position = (data - minValue)/_bucketRangeStep;
return position == 0 ? 0 : (position >= _bucketSize ? _bucketSize - 1 : position);
}
//将元素插入桶中
void insertDataToBucketContainer(int data,int minValue,int maxValue,Bucket * bucketContainer){
//计算桶的位置
int bucketPosition = calculateBucketPosition(data,minValue,maxValue);
//初始化桶元素 注意这里的资源应该是要被释放的哦
BucketItem * _bucketItem = new BucketItem;
_bucketItem->value = data;
_bucketItem->next = NULL;
//桶中没有元素
if((bucketContainer + bucketPosition)->head == NULL){
(bucketContainer + bucketPosition)->head = _bucketItem;
return;
}else{
//桶中已经有数据了
BucketItem * _exists_value = (bucketContainer + bucketPosition)->head;
if (_exists_value -> value > data) {
//第一个元素就大
(bucketContainer + bucketPosition)->head = _bucketItem;
_bucketItem->next = _exists_value;
}else{
int find = 0;
while (_exists_value ->next != NULL) {
if (_exists_value ->next -> value > data) {
_bucketItem->next = _exists_value -> next;
_exists_value -> next = _bucketItem;
find = 1;
break;
}else{
_exists_value = _exists_value -> next;
}
}
if(find == 0){
//所有都比带插入的数据小,那么,就插到末尾吧
_exists_value ->next = _bucketItem;
}
}
}
}
//将桶中的元素,依次取出,放入原数组中,记得同时释放桶中的元素
void getOutItems(Bucket * bucketContainer,int * data){
int j = 0;
for(int i = 0;i < _bucketSize; i++){
BucketItem * _bucketItem = (bucketContainer + i)->head;
while (_bucketItem != NULL) {
*(data + j) = _bucketItem->value;
j++;
BucketItem * _tmp_bucketItem = _bucketItem;
_bucketItem = _bucketItem -> next;
//释放资源
delete _tmp_bucketItem;
}
}
//释放资源
delete [] bucketContainer;
}
//排序入口
int * do_sorting(int * data,int size){
int _minValue = 0, _maxValue = 0;
//计算最大最小值,给桶分区的计算提供参考
calculateMaxAndMinValue(data,size,&_minValue,&_maxValue);
//初始化桶
Bucket * _bucket = initBucketContainer(_minValue,_maxValue);
//将元素依次插入桶中
for(int i = 0; i < size; i++){
insertDataToBucketContainer(*(data + i),_minValue,_maxValue,_bucket);
}
//取出元素
getOutItems(_bucket,data);
return data;
}
};
#endif /* defined(__p1__BucketSort__) */
算法总结:
注意,这里在求一个数组的最大值和最小值时,使用了一个技巧,将时间代价由O(2n)降低为O(2/3 n).
另外,桶排序并不是hash算法哈,最本质的区别是,桶排序中每个桶是有大小顺序的,而hash算法的桶是没有大小排序的;
第二个区别是:二者的目的也不一样,桶排序是为了对数据完成排序,最后的数据结构还是数组,而hash是为了快速查找一个元素(虽然桶排序的最终目的也是快速查找元素),其最终的数据结构是hash桶
第三个区别是:二者在快速查找数据时,桶排序最终是使用二分查找法对已排好序的数组进行查找,而hash是使用key落入桶的快速定位法,如果落入的桶中的元素非常的多,又会变成一个线程的顺序查找,性能比二分查找差,而如果桶中就一个或者有限个元素,则其查找的次数又小于二分查找,其性能又好于二分查找