您现在的位置是:首页 >其他 >桶排序 — 计数排序和基数排序网站首页其他

桶排序 — 计数排序和基数排序

善良的Leexx 2024-07-11 12:01:05
简介桶排序 — 计数排序和基数排序

计数排序
int类型数组,其中存的是员工的年龄。比如说16 - 150。对于这样的数据来讲,数据状况是受限的。此时如果将数组从小到大进行排序,该如果实现?
这个实现很简单,实现一个统计数组范围从 0 ~ 150,遍历原数组几岁就在统计数组对应的下标出的值 + 1,遍历完后,再遍历一次统计数组,看统计数组哪些下标对应的值不为0,有几说明对应的年龄就有几个人,写回到老数组即可完成排序。整体时间复杂度O(N)。

public void countSort(int[] arr) {
            int max = Integer.MIN_VALUE;
            //选出最大值
            for (int i = 0; i < arr.length; i++) {
                Math.max(max, arr[i]);
            }
            //数组长度取年龄最大值 + 1
            int[] count = new int[max + 1];

            for (int j = 0; j < arr.length; j++) {
                count[arr[j]]++;
            }

            int i = 0;
            for (int k = 0; k < count.length; k++) {
                while (count[k]-- > 0) {
                    arr[i++] = k;
                }
            }
        }

这种排序称为计数排序,计数排序所用的思想是桶排序的思想,桶就是容器的意思,题中的年龄就是桶。

这样来看,从排序特征上来分大致可分为两类:不基于比较的排序和比较排序。其中桶排序就是不基于比较的一个。
计数排序不是基于比较,根据年龄,放入不同的桶中,最后从桶中获取,一个比较行为都没有。
不基于比较的排序,一定是数据范围比较特殊。所以,不基于比较的排序的扩展性相较于基于比较的排序来讲,可扩展性比较小,因为基于比较的排序实现了一个对数器之后,所有的基于比较的排序都可以用,而不基于比较的不可以。需要根据具体的样本特征来具体的分析。

基数排序
基数排序对数据范围的约束大概在非负的,用十进制表示的数(负的找到最小值,数组整体加成正数也行)。
假设int[] {103,13,27,25,17,9}。
先将数组中最大值103找出来,算出十进制的位数(几次能将10除完),得出是3。
将其余元素不满3位的高位补0,补充之后是{103,013,027,025,017,009}。
准备0 ~ 9一共10个桶,数组从左往右,根据个位数放进对应下标的桶中。
在这里插入图片描述
放入后,从0桶开始,依次倒出并清空桶(根据个位数排序)。
在这里插入图片描述
根据十位数大小,再次放入桶中。
在这里插入图片描述
再次倒出桶中元素,并清空桶。
在这里插入图片描述
第三次,将百位数放入桶中后再次倒出。
在这里插入图片描述
倒出后,数组有序{009,013,017,025,027,103}
代码实现:
代码中,没有使用队列的方式来创建桶,而是用了另一种方式。

 public static void RadixSort(int[] arr) {
            if (arr == null || arr.length < 2) {
                return;
            }
            radixSort(arr, 0, arr.length - 1, maxbits(arr));
        }

        // L。。。R进行排序
        public static void radixSort(int[] arr, int L, int R, int digit) {
            final int radix = 10;
            int i = 0, j = 0;
            // 有多少个数准备多少个辅助空间
            int[] help = new int[R - L + 1];
            for (int l = 1; l < digit; l++) {
                int[] count = new int[radix];

                for (i = L; i <= R; i++) {
                    j = getDigit(arr[i], l);
                    //统计,数组中每个数按照个位十位等拆开后,0~9范围上每一个数共出现了几次
                    count[j]++;
                }
                //求出所有数的前缀和
                for (i = 1; i < radix; i++) {
                    count[i] = count[i] + count[i - 1];
                }
                //从尾向前遍历
                for (i = R; i >= L; i--) {
                    //再次获取当前数,并算出个位数等。。
                    j = getDigit(arr[i], l);
                    //count[j] - 1 确定出当前数应该落在数组位置的下标。
                    //比如 当前j = 2,则说明前缀和count数组中, <= 2 的时 0 ~ 1 范围,
                    // 并且我还是arr从后向前遍历arr,所以arr的位置就应该在count[j] - 1上。
                    help[count[j] - 1] = arr[i];
                    //对应位置数量 - 1
                    count[j]--;
                }

            }
        }

        public static int maxbits(int[] arr) {

            int max = Integer.MIN_VALUE;
            //找到数组中最大值
            for (int i = 0; i < arr.length; i++) {
                Math.max(max, arr[i]);
            }
            int res = 0;
            //根据最大值/10,找到十进制位数
            while (max != 0) {
                res++;
                max /= 10;
            }
            return res;
        }

        //d = 1,2,3 求出x的个位十位百位
        public static int getDigit(int x, int d) {
            return ((x / ((int) Math.pow(10, d - 1))) % 10);
        }
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。