桶排序

作者: 程序员will | 来源:发表于2019-11-01 14:36 被阅读0次

    桶排序

    桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。

    入门案例

    用一个例子来演示就是,期末考试完了老师要将同学们的分数按照从高到低排序。小哼的班上只有 5 个同学,这 5 个同学分别考了 5 分、3 分、5 分、2 分和 8 分,(满分是 10 分)。

    接下来将分数进行从大到小排序,排序后是 8 5 5 3 2。

    我们这里只需借助一个一维数组就可以解决这个问题。

    首先我们需要申请一个大小为 11 的数组 int a[11]。刚开始的时候,我们将 a[0]~a[10]都初始化为 0,表示这些分数还都没有人得过。例如 a[0]等于 0 就表示目前还没有人得过 0 分,同理 a[1]等于 0 就表示目前还没有人得过 1 分……a[10]等于 0 就表示目前还没有人得过 10 分。 picture1.3

    下面开始处理每一个人的分数,第一个人的分数是 5 分,我们就将相对应 a[5]的值在原来的基础增加 1,即将 a[5]的值从 0 改为 1,表示 5 分出现过了一次。

    picture1.4

    第二个人的分数是 3 分,我们就把相对应 a[3]的值在原来的基础上增加 1,即将 a[3]的值从 0 改为 1,表示 3 分出现过了一次。

    picture1.5

    注意啦!第三个人的分数也是“5 分”,所以a[5]的值需要在此基础上再增加 1,即将 a[5]的值从 1 改为 2。表示 5 分出现过了两次。

    picture1.6

    按照刚才的方法处理第四个和第五个人的分数。最终结果就是下面这个图啦。

    picture1.7

    你发现没有,a[0]~a[10]中的数值其实就是 0 分到 10 分每个分数出现的次数。接下来,我们只需要将出现过的分数打印出来就可以了,出现几次就打印几次,具体如下。   
      最终屏幕输出“2 3 5 5 8”

    这是桐排序最快的情况,也是最简单的情况吗,就是牺牲了大量的空间换取时间

    接下来看看常规桶排序算法思路。

    思路

    1. 设置固定空桶数
    2. 将数据放到对应的空桶中
    3. 将每个不为空的桶进行排序
    4. 拼接不为空的桶中的数据,得到结果

    为了使桶排序更加高效,我们需要做到这两点:

    1. 在额外空间充足的情况下,尽量增大桶的数量
    2. 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中

    同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。

    什么时候最快

    当输入的数据可以均匀的分配到每一个桶中。

    什么时候最慢

    当输入的数据被分配到了同一个桶中。

    示意图

    元素分布在桶中:

    img

    然后,元素在每个桶中排序:

    img

    举个例子

    假设一组数据(20长度)为

    [63,157,189,51,101,47,141,121,157,156,194,117,98,139,67,133,181,13,28,109] 
    

    现在需要按5个分桶,进行桶排序,实现步骤如下:

    1. 找到数组中的最大值194和最小值13,然后根据桶数为5,计算出每个桶中的数据范围为(194-13+1)/5=36.4
    2. 遍历原始数据,(以第一个数据63为例)先找到该数据对应的桶序列Math.floor(63 - 13) / 36.4) =1,然后将该数据放入序列为1的桶中(从0开始算)
    3. 当向同一个序列的桶中第二次插入数据时,判断桶中已存在的数字与新插入的数字的大小,按从左到右,从小打大的顺序插入。如第一个桶已经有了63,再插入51,67后,桶中的排序为(51,63,67) 一般通过链表来存放桶中数据,但js中可以使用数组来模拟
    4. 全部数据装桶完毕后,按序列,从小到大合并所有非空的桶(如0,1,2,3,4桶)
    5. 合并完之后就是已经排完序的数据

    步骤图示

    img

    代码实现

    public class BucketSort implements IArraySort {
    
        private static final InsertSort insertSort = new InsertSort();
    
        @Override
        public int[] sort(int[] sourceArray) throws Exception {
            // 对 arr 进行拷贝,不改变参数内容
            int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
    
            return bucketSort(arr, 5);
        }
    
        private int[] bucketSort(int[] arr, int bucketSize) throws Exception {
            if (arr.length == 0) {
                return arr;
            }
    
            int minValue = arr[0];
            int maxValue = arr[0];
            for (int value : arr) {
                if (value < minValue) {
                    minValue = value;
                } else if (value > maxValue) {
                    maxValue = value;
                }
            }
    
            int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1;
            int[][] buckets = new int[bucketCount][0];
    
            // 利用映射函数将数据分配到各个桶中
            for (int i = 0; i < arr.length; i++) {
                int index = (int) Math.floor((arr[i] - minValue) / bucketSize);
                buckets[index] = arrAppend(buckets[index], arr[i]);
            }
    
            int arrIndex = 0;
            for (int[] bucket : buckets) {
                if (bucket.length <= 0) {
                    continue;
                }
                // 对每个桶进行排序,这里使用了插入排序
                bucket = insertSort.sort(bucket);
                for (int value : bucket) {
                    arr[arrIndex++] = value;
                }
            }
    
            return arr;
        }
    
        /**
         * 自动扩容,并保存数据
         *
         * @param arr
         * @param value
         */
        private int[] arrAppend(int[] arr, int value) {
            arr = Arrays.copyOf(arr, arr.length + 1);
            arr[arr.length - 1] = value;
            return arr;
        }
    
    }
    

    相关文章

      网友评论

        本文标题:桶排序

        本文链接:https://www.haomeiwen.com/subject/hlwfbctx.html