桶排序--平均情况下时间代价为O(n)的排序算法

    在介绍了计数排序和基数排序两种线性时间复杂度的排序算法之后,我们来介绍第三种时间复杂度为线性,平均情况下的时间代价为O(n)的的排序算法--桶排序


一、适用情况

    这种排序假设输入数据服从均匀分布,而且需要已知待排序列的大概范围,是一种空间换时间的排序算法。


二、基本思想

    对于一个待排序列,我们已知它的大概范围的时候,我们把这个范围分为n个相同大小的子区间,也就是我们说的,我们这里用数组的每一个元素代表一个桶。然后我们就可以把输入数据分道各个桶之中,因为我们前面假设了输入数据是均匀分布的情况,所以不会出现很多数落在同一个桶的情况。当然还是会出现多个数字落在同一个桶的情况,这时我们就会为当前桶生成一个链表,以此来维护一个桶之中的多个元素(像不像解决哈希冲突的拉链法)。对于一个桶之中的多个元素,我们使用插入排序,来保证这个链表是有序的。之后我们就获得了n个桶以及这n个桶对应的有序链表,我们遍历每个桶中的链表,就获得了排序的结果。


三、运行过程

    假如现在我们需要对以下序列进行排序:


    我们首先将[0,99]这个范围平均分成10个桶,分别代表范围[0,9],[10,19],...,[90,99]。


    然后一一把这些数据放入对应的桶中:

    桶排序--平均情况下时间代价为O(n)的排序算法_第1张图片


    放入21的时候没有什么问题,接下来我们放入23,发现这个桶已经有元素了,我们把23和这个桶的元素比较,放在链表适当的位置:

桶排序--平均情况下时间代价为O(n)的排序算法_第2张图片


    后面都一样,直到我们把所有元素都放入桶中:

桶排序--平均情况下时间代价为O(n)的排序算法_第3张图片


    到这个时候我们就可以开始遍历整个n个桶,得到排序结果:0 21 23 24 32 58 79 99


四、代码实现

    我们使用Java来实现这个算法:

    public static int[] bucketSort(int[] arr, int bucketSize){
        int[] result = new int[arr.length];//排序好的结果数组
        @SuppressWarnings("unchecked")
        LinkedList[] bucket = new LinkedList[bucketSize];//桶数组
        for(int i = 0; i < bucketSize; i++){
            //使bucket[i]称为一个空链表
            bucket[i] = new LinkedList();
        }
        //将arr[i]插入到 bucket数组 如果数组位置上已经有元素 则插入到其位置的链表的指定位置
        for(int i = 0; i < arr.length; i++){
            int bucketLocation = arr[i]/bucketSize;
            int insertLocation = 0; //待插入位置
            for(insertLocation = 0; insertLocation < bucket[bucketLocation].size() && bucket[bucketLocation].get(insertLocation) < arr[i] ; insertLocation++);
            bucket[bucketLocation].add(insertLocation, arr[i]);
        }
        for(int i = 0,j = 0; i < bucket.length; i++){
            Iterator iterator = bucket[i].iterator();
            while(iterator.hasNext()){
                result[j++] = iterator.next();
            }
        }
        return result;
    }

    这里需要注意一点,我的代码桶中的链表使用的是java.util.LinkedList这个链表,它的add会遍历一次链表,因此增加了算法的时间。事实上我们完全可以自己写自己的链表,确定插入位置insertLocation之后直接把结点插入指定位置,也就是找到待插入位置的前一个结点,直接插入,而不需要再遍历一遍链表才插入。


五、时间复杂度分析和时间比较

    桶排序是在比较同一个桶内的元素的时候使用了插入排序,插入排序是稳定的,当然桶排序也是稳定的。这里不做过多证明了,直观上来看桶排序的时间代价T(n) = Θ(n) + O(n0 ^ 2) + O(n1 ^ 2) + ... + O(n(n-1)^2),其中n0,n1,n(n-1)中表示n和n的下标。但是平均情况下的时间代价是Θ(n),也就是是线性时间完成的。

你可能感兴趣的:(算法)