六、排序算法四(桶排序)
一、桶排序算法的引入。
之前我们已经说过了计数排序的算法。
这个时候我们如果有这样的一个待排序数据序列:
int x[14]={-10, 2, 3, 7, 20, 23, 25, 40, 41, 43,60, 80, 90, 100};
我们如果按照计数排序的算法,那么待排序数据的范围是:-10 到 100
我们为了实现对于这个数据集的遍历,这个时候需要额外的开辟一个大小为(100- (-10)+ 1)的空间,这个时候的空间复杂度达远远超过我们的预计,也就是这个造成了空间的浪费。
所以我们可以说,计数排序还存在一个缺点:即数据里面的max 和min 的差值相比于待排序数据的数目来说,不能过大,否则会造成空间的浪费
桶排序算法相当于做了一个对于计数排序算法做了一个升级。
只不过计数排序算法计数是对于每一个排序数字进行的,而桶排序算法则将这个范围放宽,变成了一个范围。
二、桶排序算法的过程。
1、比如对于上面的一个待排序数据,我们需要把它划分为一系列的范围,从min 到 min,这就相当于有了一个一个的“桶”。
2、对应的范围的数据放入对应范围的桶里面。
3、然后对于每一个范围的数据采用其他的排序算法进行排序,使得每一个桶内部的数据都是有序的,移动到已经有序的数据集中。
上面有两个步骤比较关键:
第一个是对于桶的范围的划分,这需要一个映射的规则,如果我们把桶划分的较小,这个时候几乎每一个数据都是一个桶;了,空间浪费。如果划分的太大,所有的数据都到了一个桶里面去,相当于没有划分。
第二个是每一个桶的内部的排序算法的选择,这个影响最后总体的桶排序算法的稳定性。
三、例子
对于上面的一个待排序数据,这个时候我们已经知道了max=100, min=-10.
随后我们设计一个映射的规则,比如设计一个规则,使得 -10到 50 分为一个桶,50到100分为一个桶。
我们可以设计这样的一个规则:
ƒ(x)=x/30 - min/30
这样我们就可以把上面的数组分成了四组:
| 根据结果分组 | 数据 |
| 0 | -10, 2, 3, 7 |
| 1 | 20, 23 ,25 ,40, 41 |
| 2 | 60 |
| 3 | 80, 90, 100 |
然后就可与根据分组分别进行排序。
四、算法
/*算法:桶排序(C++版本)*/ #include <iostream> #include <vector> #include <algorithm> #include <math.h> using namespace std; //这个算法的作用是对于数组A中位于位置A[l]到A[h]中的元素分为size个桶去排序 void bksort(float A[], int l, int h, int size) { //int len = h - l + 1; vector<float> *b=new vector<float>[size];//有size个数据,就分配size个桶 float min = A[l], max = A[l]; for (int i = l; i <= h; i++) {//遍历得到最小值 if (A[i] < min) min = A[i]; if (A[i] > max) max = A[i]; } cout<<"max "<<max<<"min "<<min<<endl; float width = (max - min) / size; cout<<"width"<<width<<endl; for (int i = l; i <= h; i++) { int bi = floor((A[i] - min) / width);//元素A[i]的桶编号,这里修改映射规则 if (bi >= size) bi = size - 1;//这个是处理最大值的情况,最大值得到的bi是size b[bi].push_back(A[i]);//将元素A[i]压入桶中 } for (int i = 0; i < size; i++) sort( b[i].begin(), b[i].end());//桶内排序 int idx = l;//指向数组A的下标 for (int i = 0; i < size; i++) {//遍历桶 for (int j = 0; j < b[i].size(); j++) {//遍历桶内元素 A[idx++] = b[i][j]; } } } int main() { float A[] = { 0.78,0.17,0.39,0.26,0.72,0.94,0.21,0.12,0.23,0.68 }; bksort(A, 2, 9, 3); for (int i = 0; i < 10; i++) cout << A[i] << " "; }
错误说明:
1、在声明数组b的时候,我最开始是这样声明的:
vector<float> b[size]
用DevC++编译是没有问题的,但是用VS编译是有问题的。
因为在DevC++是允许使用变量声明数组长度的,但是VS里面则是不允许的,所以修改如下:
vector<float> *b=new vector<float>[size];
2、在设计映射规则的时候,我最开始没有考虑max作为数据输入的时候,得到的结果是size, 这个超出了数组的长度,所以在函数里面添加了下面一句:
if (bi >= size) bi = size - 1;//这个是处理最大值的情况,最大值得到的bi是size
为了防止数据溢出。
五、复杂度分析
复杂度主要是取决于排序算法。
如果排序算法是快速排序,则可以达到Θ( n ) (大佬们的计算结果)
六、参考链接:
复杂度计算看这个:https://blog.csdn.net/bqw18744018044/article/details/81738883
算法的过程描述借鉴的是这个:https://www.jianshu.com/p/204ed43aec0c

浙公网安备 33010602011771号