【算法】————8、桶排序

算法简介

桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排

算法描述和实现

具体算法描述如下:

  • <1>.设置一个定量的数组当作空桶;
  • <2>.遍历输入数据,并且把数据一个一个放到对应的桶里去;
  • <3>.对每个不是空的桶进行排序;
  • <4>.从不是空的桶里把排好序的数据拼接起来。

Javascript代码实现:

/*方法说明:桶排序
@param  array 数组
@param  num   桶的数量*/
function bucketSort(array, num) {
    if (array.length <= 1) {
        return array;
    }
    var len = array.length, buckets = [], result = [], min = max = array[0], regex = '/^[1-9]+[0-9]*$/', space, n = 0;
    num = num || ((num > 1 && regex.test(num)) ? num : 10);
    console.time('桶排序耗时');
    for (var i = 1; i < len; i++) {
        min = min <= array[i] ? min : array[i];
        max = max >= array[i] ? max : array[i];
    }
    space = (max - min + 1) / num;
    for (var j = 0; j < len; j++) {
        var index = Math.floor((array[j] - min) / space);
        if (buckets[index]) {   //  非空桶,插入排序
            var k = buckets[index].length - 1;
            while (k >= 0 && buckets[index][k] > array[j]) {
                buckets[index][k + 1] = buckets[index][k];
                k--;
            }
            buckets[index][k + 1] = array[j];
        } else {    //空桶,初始化
            buckets[index] = [];
            buckets[index].push(array[j]);
        }
    }
    while (n < num) {
        result = result.concat(buckets[n]);
        n++;
    }
    console.timeEnd('桶排序耗时');
    return result;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(bucketSort(arr,4));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

JAVA:

public static void bucketSort(int[] arr){
    //分桶,这里采用映射函数f(x)=x/10。
    //输入数据为0~99之间的数字
    int bucketCount =10;
    Integer[][] bucket = new Integer[bucketCount][arr.length];  //Integer初始为null,以与数字0区别。
    for (int i=0; i<arr.length; i++){
        int quotient = arr[i]/10;   //这里即是使用f(x)
        for (int j=0; j<arr.length; j++){
            if (bucket[quotient][j]==null){
                bucket[quotient][j]=arr[i];
                break;
            }
        }
    }
    //小桶排序
    for (int i=0; i<bucket.length; i++){
            //insertion sort
            for (int j=1; j<bucket[i].length; ++j){
                if(bucket[i][j]==null){
                    break;
                }
                int value = bucket[i][j];
                int position=j;
                while (position>0 && bucket[i][position-1]>value){
                    bucket[i][position] = bucket[i][position-1];
                    position--;
                }
                bucket[i][position] = value;
            }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
    }
    //输出
    for (int i=0, index=0; i<bucket.length; i++){
        for (int j=0; j<bucket[i].length; j++){
            if (bucket[i][j]!=null){
                arr[index] = bucket[i][j];
                index++;
            }
            else{
                break;
            }
        }
    }
}

算法性能/复杂度

桶排序的时间复杂度可以从每一步分开分析。

1.分桶的过程,遍历每个元素、计算f(x),将x放到桶中,共3n次计算,显然是O(n)复杂度;

2.最后输出也是O(n)复杂度;

3.关键是小桶内排序的过程:即使使用先进的比较排序算法,也不可绕开O(n㏒n)的下限。因此,每个小桶的内部复杂度为n(k㏒k),总得复杂度为∑(ki*㏒ki)[i=1...m],其中m为桶的个数,ki为每个桶的元素数。尽量减少桶内数据的数量是提高效率的唯一办法(因为基于比较排序的最好平均时间复杂度只能达到O(N*logN)了)。因此,有两种方法:

1)使用更为平均的划分,使得不至于某个小桶的数据极多;

2)使用更多的桶,以减少每个桶数据的数量。极限状况下,每个桶只有一个数据,这样就完全没有比较操作。但是,在数据极多的情况下,这样是非常不现实的,会造成严重的空间消耗。这时候就需要权衡时空间复杂度了。

总结起来,设数据共有N个,桶有M个,则桶排序平均复杂度为:

O(N)+O(N)+O((N/M)*㏒(N/M))=O(N+N*(logN-logM))=O(N+N*logN-N*logM)

最优情形下,桶排序的时间复杂度为O(n)

桶排序的空间复杂度通常是比较高的,额外开销为O(N+M)(因为要维护M个数组的引用)。

算法稳定性

可以看出,在分桶和从桶依次输出的过程是稳定的。但是,由于我们在第3步使用了其他算法,所以,桶排序的稳定性依赖于这一步。如果我们使用了快排,显然,算法是不稳定的。

算法适用场景

桶排序在数据量非常大,而空间相对充裕的时候是很实用的,可以大大降低算法的运算数量级。此外,在解决一些特殊问题的时候,桶排序可能会起到意想不到的结果。参考资料中列出了一种。

示意图:

桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。

  • 最佳情况:T(n) = O(n+k)
  • 最差情况:T(n) = O(n+k)
  • 平均情况:T(n) = O(n2)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FLy_鹏程万里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值