算法与数据结构-桶排序

桶排序:

在桶排序思想下的排序都是不基于比较的排序,运用范围有限,需要样本的数据状况满足桶的划分。下面介绍桶排序思想下的两个排序
(1)计数排序 (2)基数排序

时间复杂度

O(N)

计数排序:

之前介绍的所有排序算法如插入排序,堆排序等都是基于比较的排序,除了这些排序算法外,还有不基于比较的排序:
比如将公司员工的年龄进行排序,考虑到实际年龄的范围可以开辟一个大小为100的数组,将所有员工的年龄进行统计并添加到数组中,比如数组第0个位置为年龄为1员工的个数,依次类推。
最后根据数组上每个位置的值(即频数)将年龄全部展开,就可以得到一个排好序的年龄数组。 上述这种排序叫计数排序,

代码实现:

public class CountSort {
    public static void countSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(max, arr[i]);
        }
        int[] bucket = new int[max + 1];
        for (int i = 0; i < arr.length; i++) {
            bucket[arr[i]]++;
        }
        int i = 0;
        for (int j = 0; j < bucket.length; j++) {
            while (bucket[j]-- > 0) {
                arr[i++] = j;
            }
        }
    }
}

基数排序:

假设需要排序的数组为[17,13,25,100,72]:
1.确定所有数中最大数的位数为几位,该数组中最大数为100,即为3位。将数组中所有数进行填充到3位,得到[017,013,025,100,072]
2.所有数字中数字类别为0~9,所以需要0-9这10个桶。使用队列作为桶的数据结构,这样桶在输出数字时是先进先出的(可以是栈,数组等其他结构)
3.根据填充后的数组中不同位数的大小将数字放入桶中:
3.1 先看个位数,可以得到[7,3,5,0,2],根据个位数值将填充数字分别放入这8个桶中:
0号:[100] 1号:[] 2号:[072] 3号:[013]
4号:[] 5号:[025] 6号:[] 7号:[017]
将桶中数字按桶的顺序依次返回得到新数组[100,072,013,025,017]
3.2 再看十位数,可以得到[0,7,1,2,1],根据十位数值将填充数字分别放入这8个桶中:
0号:[100] 1号:[013,017] 2号:[025] 3号:[]
4号:[] 5号:[] 6号:[] 7号:[072]
将桶中数字按桶的顺序依次返回得到新数组[100,013,017,025,072]
3.3 最后看百位数,可以得到[1,0,0,0,0],根据百位数值将填充数字分别放入这8个桶中:
0号:[013,017,025,072] 1号:[100] 2号:[] 3号:[]
4号:[] 5号:[] 6号:[] 7号:[]
将桶中数字按桶的顺序依次返回得到新数组[013,017,025,072,100]

代码实现和分析过程是有一定区别的,假设数组arr=[13,21,11,52,62]:
1.创建一个count数组(大小为10)用于记录每个个位数对应的数字个数,所以count数组为[0,2,2,1,0,0,0,0,0,0]
2.计算当前count数组的不同位置值的前缀和并填入count数组中,[0,2,4,5,5,5,5,5,5,5]。其中,2=0+2,4=0+2+2,5=0+2+2+1,以此类推。此时count数组的含义是与1中的不一样,比如4表示的是原数组中个位数<=2的数字个数有4个
3.此时从右往左遍历原数组,即先看62,它的个位数是2,所以排序后放置的位置是bucket数组(辅助数组,用于暂时储存当前排序的数)的第3位上
为什么?这个3=4-1,4为count在个位数为2上的值,因为数组是从0开始的,所以将62放置在第3位置上(即个位数为2的数字还有3个,要放在第0、1、2位置上),这样正好符合原数组中个位数<=2的数字个数有4个,从右开始遍历正好符合进桶的顺序是先进先出。此时将count数组中个位数为2上的值需要减1,注意count其他位置上的数不需要改变。最终bucket数组为[(21,11),(52,62),(13)]。每一个括号内的数可以视为一个桶中的数
4.将bucket数组中的数重新赋值给arr数组,再对其十进制、百进制位…继续上述1~3的操作

代码实现:

public class RadixSort {
    // 只针对非负值
    public static void radixSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        radixSort(arr, 0, arr.length - 1, maxbits(arr));

    }

    // arr[L...R]排序
    public static void radixSort(int[] arr, int L, int R, int digit) {
        // radix设置为10,因为数字的范围(即桶的个数)为0~9
        final int radix = 10;
        int i = 0, j = 0;
        int[] bucket = new int[R - L + 1];
        // 根据十进制位的大小决定进出桶几次
        for (int d = 1; d <= digit; d++) {
            // count表示一个前缀和数组
            // count[0]:当前位(d位)是(0)的数字有多少个
            // count[1]:当前位(d位)是(0和1)的数字有多少个
            // count[i]:当前位(d位)是(0~i)的数字有多少个
            int[] count = new int[radix];

            // 先计算不同桶需要放置数的个数
            for (i = L; i < R; i++) {
                j = getDigit(arr[i], d);
                count[j]++;
            }
            // 再计算前缀和
            for (i = 1; i < radix; i++) {
                count[i] = count[i] + count[i - 1];
            }

            // 将原数组从右到左重新放置到bucket数组(一个辅助数组)中
            for (i = R; i >= L; i--) {
                j = getDigit(arr[i], d);
                bucket[count[j] - 1] = arr[i];
                count[j]--;
            }
            // 将bucket数组重新放置到原数组中
            for (i = L, j = 0; i <= R; i++, j++) {
                arr[i] = bucket[j];
            }

        }
    }

    // 获取数字x在第d位(从右向左)十进制上的值(可以视为实现了对数组中每一个数的填充)
    public static int getDigit(int x, int d) {
        return ((x / ((int) Math.pow(10, d - 1))) % 10);
    }

    // 计算数组中最大值有几个十进制位
    public static int maxbits(int[] arr) {
        int max = Integer.MIN_VALUE;
        // 选取数组中的最大值
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(max, arr[i]);
        }
        int res = 0;
        while (max != 0) {
            res++;
            max /= 10;
        }
        return res;
    }
}

tips:

相较于计数排序,基数排序对于数据范围的要求就降低了很多,就算是亿级别的数需要桶的数量还是可控的 。但是对于数据类型就有一定要求了,必须有进制的数据对象才能使用该方法进行排序,再次验证不基于比较的排序是基于数据状况的排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值