六、排序算法四(桶排序)

一、桶排序算法的引入。

  之前我们已经说过了计数排序的算法。

  这个时候我们如果有这样的一个待排序数据序列:

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

 

  

posted @ 2019-11-18 23:40  星光夜  阅读(231)  评论(0)    收藏  举报