【算法面试宝典】十大经典排序算法 - 桶排序

1 解题思路

        桶排序是最简单的排序算法之一,桶排序和计数排序、基数排序有很多相似和渊源之处。桶排序重要的是它的思路,而不是具体的实现。不同待排序元素,桶排序的具体实现会有差异。

        桶排序的工作原理是把待排序的元素放入对应的桶里。每个桶再进行单独的排序(可以是快速排序、可以是递归排序、也可以用递归的方式继续使用桶排序进行排序)。桶排序需要经过四步:

1)按照元素特性设计好桶;

2)把待排序元素放入对应的桶中;

3)对每个桶内的元素进行排序;

4)合并桶中的元素得到有序序列。

        既然是排序,最终的结果要么是从大到小,要么是从小到大,桶排序首先把桶的位置排好,然后一次把元素分别放入各个桶中。确定桶的数量有很多种,可以根据待排序元素的特性来定义桶的数量和元素放入桶的规则。通常根据待排序元素整除的方法将其均匀放入桶中。

示例:

输入 nums = {7,10,21,6,13,39,58,22,40,52,32,50,46,29}   

输出 nums = {6,7,10,13,21,22,29,32,39,40,46,50,52}

观察数组中元素的特性,可以设计出放入桶编号的规则为:元素值/10。这样每个元素都可以通过整除的方法放至对应的桶中。而左侧所有的桶内的元素都要比右侧桶内元素的值小。放置的过程如下图所示:

 在刚刚放入桶中的时候,各个桶的大小相对可以确定,左侧比右侧要小,但是桶内还是无序的,对各个桶内分别进行排序,桶内元素排序的排序算法可以自行选择:

再依次按照桶的顺序以及桶内序列得到一个最终的有序序列。 

2 编码实现

编码实现一:

可以使用计数排序的思想,桶里放的是元素出现的次数。桶的编号是元素的值。编码如下所示:

public static void bucketSort(int[] nums){

        //找到最大值和最小值
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int i=0;i<nums.length;i++){
            max = Math.max(max,nums[i]);
            min = Math.min(min,nums[i]);
        }
        int k = max - min+1;//计算桶的个数
        int[] tmp = new int[k];//定义出K个桶
        //计算出元素出现的次数,计入桶中
        for (int i=0;i<nums.length;i++){
            tmp[nums[i]-min]++;
        }

        //将待排序的数组放入自己的位置
        int x = 0;
        for (int i=0;i<tmp.length;i++){
            while (tmp[i]-->0){
                nums[x++] = i+min;
            }
        }

    }

编码实现二:

使用链表存储桶内的元素,链表中的元素依次排序,再合并链表最终得到有序数组。

public static void bucketSort1(int[] nums){
        //找到最大值和最小值
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int i=0;i<nums.length;i++){
            max = Math.max(max,nums[i]);
            min = Math.min(min,nums[i]);
        }
        int k = (max-min)/nums.length+1;
        ArrayList<ArrayList> list = new ArrayList<ArrayList>(k);
        for (int i=0;i<k;i++){
            list.add(new ArrayList<Integer>());
        }

        for (int i=0;i<nums.length;i++){
            list.get((nums[i]-min)/nums.length).add(nums[i]);
        }

        for (int i=0;i<list.size();i++){
            Collections.sort(list.get(i));
        }

        System.out.println(list.toString());
    }

3 时间复杂度和空间复杂度

时间复杂度

        假设有n个待排序数字。分到m个桶中,如果分配均匀这样平均每个桶有n/m个元素。桶排序的算法时间复杂度有两部分组成:

  • 遍历处理每个元素,O(n)级别的普通遍历
  • 每个桶内再次排序的时间复杂度总和

对于第一个部分,大家都应该理解最后排好序的取值遍历一趟的O(n),而第二部分咱们可以进行这样的分析:

  • 如果桶内元素分配较为均匀假设每个桶内部使用的排序算法为快速排序,那么每个桶内的时间复杂度为(n/m) log(n/m)。有m个桶,那么时间复杂度为m * (n/m)log(n/m)=n (log n-log m).

所以最终桶排序的时间复杂度为:O(n)+O(n*(log n- log m))=O(n+n*(log n -log m)) 其中m为桶的个数。我们有时也会写成O(n+c),其中c=n*(log n -log m);

在这里如果到达极限情况n=m时。就能确保避免桶内排序,将数值放到桶中不需要再排序达到O(n)的排序效果,当然这种情况属于计数排序,后面再详解计数排序记得再回顾。

空间复杂度

        桶排序是典型的使用空间换取时间的排序算法。是计数排序的扩展。桶排序要定义出多个桶,能够容纳所有的待排序元素,如果桶内使用快速排序等空间复杂度为O(1)的排序算法,那么桶排序的空间复杂度取决于桶的容量综合,S(n) = O(n+1)。如果桶内使用的是其他需要额外空间的排序算法,那么桶排序的空间复杂度是两个空间复杂度的综合,S(n) = O(n+k)

猜你喜欢

转载自blog.csdn.net/u010482601/article/details/121562433