数据结构小白之基数排序

1.基数排序简单介绍

基数排序是通过分析元素的各个位置的具体值,将要排序的元素分配到某些桶中,达到排序的效果

它是稳定性的排序,是使用空间来换取时间的经典例子

它是桶排序的扩展

2.基数排序的思想

在每次排序的时候,创建序号为0-9的10个桶,将所有待比较的数值统一为同样的数位长度,数位较短的数前面补0,然后从最低位开始,依次进行排序。这样从最低位直到最高位排序完成之后,数列就变成了一个有序序列

3.举个栗子

53, 3, 542, 748, 14, 214 使用基数排序, 进行升序排序
第一轮: 542 53 3 14 214 748 -- 按照个位排序
第二轮: 3 14 214 542 748 53 -- 按照十位排序
第三轮  3 14 53 214 548 713 -- 按照百位排序
此时已经排序完毕.

来张图康康

4.代码一览

准备原数组arr

准备一个二维数组标识哪个容器中的哪个数据 bucket[10][arr.length]

准备一个数组来标识桶中实际的数据值

  int[][] bucket = new int[10][arr.length];
        //为了记录每个桶中实际存放了多少个数据,我们定义一个一维数组记录各个桶的有效数据
        //bucketElementCount[0] 就是bucket[0]中记录数据的数量
        int[] bucketElementCount = new int[10]; //数组的默认值为0
        //开始处理第一轮操作,取出每个元素的个位

开始遍历数组元素,并放在不同的桶中

 for (int j = 0; j < arr.length; j++) {
            //得到个位数
            int digitOfElement = arr[j] / 1 % 10;
            //将当前的数据放入到对应桶中
            /**
             * bucket[digitOfElement]: 表示放在哪个容器中
             * bucketElementCount[digitOfElement] 默认值为0 表示每个桶默认的数据个数
             * */
            bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j];
            bucketElementCount[digitOfElement]++;
        }

最后将桶中的数据取出,归还给原来的数组arr

  //按照桶的数据(取出数据放入数组中)
        int index = 0;
        //开始遍历每一个桶,并将桶中的数据放入到元素中
        //bucketElementCount:10
        for (int k = 0; k < bucketElementCount.length; k++) {
            //如果桶中有数据才放入到原数组中
            if (bucketElementCount[k] != 0) {
                //
                for (int l = 0; l < bucketElementCount[k]; l++) {
                    //取出数据放入到原来数组
                    arr[index++] = bucket[k][l];
                }
            }
            //第一轮处理后,需要将每个bucketElementCounts清零
        }
        System.out.println("当前的桶内情况为: " + Arrays.toString(bucketElementCount));
        for (int i : bucketElementCount) {
            bucketElementCount[i] = 0;
        }
        System.out.println("第一轮对个位的排序处理 arr=" + Arrays.toString(arr));
    }

以上就是第一轮的操作了,接下来两轮如法炮制

//第二轮排序(针对每个元素的十位进行排序处理)
    public static void radixSecondSort(int[] arr) {
        int[][] bucket = new int[10][arr.length];
        //为了记录每个桶中实际存放了多少个数据,我们定义一个一维数组记录各个桶的有效数据
        //bucketElementCount[0] 就是bucket[0]中记录数据的数量
        int[] bucketElementCount = new int[10]; //数组的默认值为0
        //开始处理第二轮操作,取出每个元素的十位
        for (int j = 0; j < arr.length; j++) {
            int digitOfElement = arr[j] / 10 % 10;
            //将当前的数据放入到对应桶中
            /**
             * bucket[digitOfElement]: 表示放在哪个容器中
             * bucketElementCount[digitOfElement] 默认值为0 表示每个桶默认的数据个数
             * */
            //开始放置数据
            bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j];
            bucketElementCount[digitOfElement]++;
        }


        //按照桶的数据(取出数据放入数组中)
        int index = 0;
        //开始遍历每一个桶,并将桶中的数据放入到元素中
        //bucketElementCount:10
        for (int k = 0; k < bucketElementCount.length; k++) {
            //如果桶中有数据才放入到原数组中
            if (bucketElementCount[k] != 0) {
                //
                for (int l = 0; l < bucketElementCount[k]; l++) {
                    //取出数据放入到原来数组
                    arr[index] = bucket[k][l];
                    index++;
                }
            }

        }
        System.out.println("当前的桶内情况为: " + Arrays.toString(bucketElementCount));
        //清空bucketElementCount 为下一次做准备
        for (int i : bucketElementCount) {
            bucketElementCount[i] = 0;
        }
        System.out.println("第二轮对十位的排序处理 arr=" + Arrays.toString(arr));
    }

    //第三轮排序(针对每个元素的百位进行排序处理)
    public static void radixThirdSort(int[] arr) {
        int[][] bucket = new int[10][arr.length];
        //为了记录每个桶中实际存放了多少个数据,我们定义一个一维数组记录各个桶的有效数据
        //bucketElementCount[0] 就是bucket[0]中记录数据的数量
        int[] bucketElementCount = new int[10]; //数组的默认值为0
        //开始处理第二轮操作,取出每个元素的十位
        for (int j = 0; j < arr.length; j++) {
            int digitOfElement = arr[j] / 100 % 10;
            //将当前的数据放入到对应桶中
            /**
             * bucket[digitOfElement]: 表示放在哪个容器中
             * bucketElementCount[digitOfElement] 默认值为0 表示每个桶默认的数据个数
             * */
            //开始放置数据
            bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j];
            bucketElementCount[digitOfElement]++;
        }


        //按照桶的数据(取出数据放入数组中)
        int index = 0;
        //开始遍历每一个桶,并将桶中的数据放入到元素中
        //bucketElementCount:10
        for (int k = 0; k < bucketElementCount.length; k++) {
            //如果桶中有数据才放入到原数组中
            if (bucketElementCount[k] != 0) {
                //
                for (int l = 0; l < bucketElementCount[k]; l++) {
                    //取出数据放入到原来数组
                    arr[index] = bucket[k][l];
                    index++;
                }
            }

        }
        System.out.println("当前的桶内情况为: " + Arrays.toString(bucketElementCount));
        //清空bucketElementCount 为下一次做准备
        for (int i : bucketElementCount) {
            bucketElementCount[i] = 0;
        }
        System.out.println("第三轮对百位的排序处理 arr=" + Arrays.toString(arr));
    }

在写完三轮循环后,发现了三轮循环有着很多很多共同点 唯一的区别就是判断是以个位 or 十位 or 百位进行区分,换言之

就是如何根据最大数的位数进行数据的区分

个位 num/1%10

十位 num/10%10

百位 num/100%10

...

所以我们需要解决如何寻找最大数的位数,然后再去由此外套一层for循环,得到最终的基数排序算法

/**
     * 得到数组中最大数的位数
     * int max=arr[0];//假设第一个数就是最大数
     * for(int i=1;i<arr.length;i++){
     * if(arr[i]>max){
     * max=arr[i];
     * }
     * }
     * //得到最大数是几位数(将max强转为string 然后求出长度)
     * int maxLength=(max+"").length();
     */

最终轮代码:

  public static void radixFinalSort(int[] arr) {
        //寻找数组的一些属性
        int max = arr[0];//假设第一个数就是最大数
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        //得到最大数是几位数
        int maxLength = (max + "").length();
        int[][] bucket = new int[10][arr.length];
        //为了记录每个桶中实际存放了多少个数据,我们定义一个一维数组记录各个桶的有效数据
        //bucketElementCount[0] 就是bucket[0]中记录数据的数量
        int[] bucketElementCount = new int[10]; //数组的默认值为0


        //最外层循环
        for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
            for (int j = 0; j < arr.length; j++) {
                int digitOfElement = arr[j] / n % 10;
                //将当前的数据放入到对应桶中
                /**
                 * bucket[digitOfElement]: 表示放在哪个容器中
                 * bucketElementCount[digitOfElement] 默认值为0 表示每个桶默认的数据个数
                 * */
                //开始放置数据
                bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j];
                bucketElementCount[digitOfElement]++;
            }


            //按照桶的数据(取出数据放入数组中)
            int index = 0;
            //开始遍历每一个桶,并将桶中的数据放入到元素中
            //bucketElementCount:10
            for (int k = 0; k < bucketElementCount.length; k++) {
                //如果桶中有数据才放入到原数组中
                if (bucketElementCount[k] != 0) {
                    //
                    for (int l = 0; l < bucketElementCount[k]; l++) {
                        //取出数据放入到原来数组
                        arr[index++] = bucket[k][l];
                    }
                }
                bucketElementCount[k]=0;
            }
            System.out.println("第"+(i+1)+"轮"+"排序处理为"+Arrays.toString(arr));
        }
    }

主函数:
 

 public static void main(String[] args) {
        int arr[] = {53, 3, 542, 748, 14, 214};
//        radixFirstSort(arr);
//        radixSecondSort(arr);
//        radixThirdSort(arr);
        radixFinalSort(arr);
    }

注:由于基数排序是一个使用空间换取时间的栗子,所以在数据量特别大的时候不建议使用

假设 有八千万个int值进行排序 运存为(80000000*11*4/1024/1024/1024)3.3GB,会导致OutOfMemoryError: Java heap space

所以数据量过大的时候不建议使用基数排序

发布了193 篇原创文章 · 获赞 70 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/Lzinner/article/details/102998771
今日推荐