数据结构——基数排序(算法)

末鹿安然 提交于 2020-01-16 01:53:59
基数排序(桶排序)介绍:
  • 基数排序(radix sort)属于“分配式排序”,又称桶子法,它是通过键值的各个位的值,将要排序的元素分配到某些“桶”中,达到排序的作用
  • 基数排序法是属于稳定性排序,基数排序法是效率高的稳定性排序法。
  • 基数排序是桶排序的扩展。
  • 基数排序是1887年赫尔曼·何乐礼发明的,它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。
基本思想:
  • 将所有待比较数值统一为同样数位长度,数位较短的数前面补0,然后从最低位开始,依次进行依次排序,这样从最低位排序一直到最高位排序完成以后,数列就变成了一个有序序列。
原理图

在这里插入图片描述

原理代码示例
    private static void radixSort(int[] array) {
        /*定义一个二维数组,表示10个桶,每个桶是一位数组*/
        /*说明:
         * 1、二维数组包含10个以为数组
         * 2、为了防止在放入数据的时候,数据溢出,则每个一位数组(桶),大小定义为array.length
         * 3、基数排序是使用空间换时间的经典算法*/
        int[][] bucket = new int[10][array.length];
        /*为了记录每个桶中,实际存放了多少个数据,定义一个一维数组记录每个桶中的数组个数
         * eg:bucketElementsCount[0],记录的是放入bucket[0]中的数据个数*/
        int[] bucketElementsCount = new int[10];
        /*获取数组中最大值,默认数组内的第一个元素为最大值*/
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
        }
        /*获取最大数据的最大位数*/
        int lengthMax = (max + "").length();
        /*循环数据的长度,第一轮取个位,第二轮取十位,依次类推*/
        for (int i = 0, m = 1; i < lengthMax; i++, m *= 10) {
            for (int j = 0; j < array.length; j++) {
                /*取相应位数的模值*/
                int digitalNumber = array[j] / m % 10;
                /*根据模值将数据依次放入对应的桶中*/
                bucket[digitalNumber][bucketElementsCount[digitalNumber]++] = array[j];
            }
            /*用于记录数组中的索引位置*/
            int index = 0;
            /*依次取出桶中的元素,还原至数组中*/
            for (int k = 0; k < bucket.length; k++) {
                /*判断各个桶中的元素是否为0,不为0 则将数据还原至数组*/
                if (bucketElementsCount[k] != 0) {
                    /*bucketElementsCount[k]代表编号为k的桶中的数据个数*/
                    for (int n = 0; n < bucketElementsCount[k]; n++) {
                        array[index++] = bucket[k][n];
                    }
                    /*数据全部还原之后,将对应编号桶的数据个数置为0,避免影响下一次数据还原,
                     * 在整个二维数组的赋值过程中,是覆盖过程*/
                    bucketElementsCount[k] = 0;
                }
            }
        }
    }

以上代码过于繁杂,最空间的要求极高,一旦数据量过大之后,很容易出现OutOfMemoryError错误,故原代码可进行简化,

    private static void radixSortSimple(int[] arr) {
        //临时数组
        Integer[] temp = new Integer[arr.length];
        //找出目标数组中的最大值、最小值,用于计算有多少位
        int maxValue = arr[0];
        int minValue = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > maxValue) {
                maxValue = arr[i];
            }
            if (arr[i] < minValue) {
                minValue = arr[i];
            }
        }
        maxValue = Math.max(Math.abs(maxValue), Math.abs(minValue));
        /*求出数据的最大位数*/
        int maxLen = (maxValue + "").length();
        int base = 10;
        /*从个位到最高位*/
        for (int i = 0, pow = 1; i < maxLen; i++, pow *= base) {
            /*定义一个一维数组,代表19个桶,用于存储各个桶中数据的个数,
            负数的存在,故需要19个桶*/
            int[] bucket = new int[base * 2 - 1];

            for (int j = 0; j < arr.length; j++) {
                /*根据余数来计算桶的编号,并存储各个桶中数据的总数
                 * 计算桶的编号:arr[j] / pow % base
                 * 计算对应编号在数组内的索引:arr[j] / pow % base + base - 1
                 * bucket[1] = 8,代表编号为 -8 这个桶内一共有8个元素*/
                bucket[arr[j] / pow % base + base - 1]++;
            }

            /*桶是有序的,将各个桶中的数字个数,转化成各个桶中最后一个数字的下标索引*/
            for (int j = 1; j < bucket.length; j++) {
                bucket[j] += bucket[j - 1];
            }

            /*桶排序属于稳定排序,当数据相同的时候,原数组排在后面的任然会排在后面
             * 故此处的循环必须从原数组的末端向前扫描*/
            for (int j = arr.length - 1; j >= 0; j--) {
//                /*获取数组中的数据*/
//                int i1 = arr[j];
//                /*对数据取模*/
//                int i2 = i1 / pow % base;
//                /*获取桶数组的index*/
//                int i3 = i2 + base - 1;
//                /*获取桶数据*/
//                int i4 = bucket[i3];
//                /*由于数组index是以0开始的,统计数据是以1开始的,故在对中间数据赋值的时候,index需要偏移一位*/
//                temp[--i4] = arr[j];
//                /*数据取出之后,需要修改桶中数据统计值,进行减一操作,*/
//                bucket[i3] = i4;
                /*以上部分是对这段代码的说明*/
                int len = --bucket[arr[j] / pow % base + base - 1];
                temp[len] = arr[j];
                System.out.println("第" + (j + 1) + "次中间数组temp:" + Arrays.toString(temp));
            }
            /*将中间数组的元素拷贝至数组*/
            for (int j = 0; j < arr.length; j++) {
                arr[j] = temp[j];
            }
            System.out.println(Arrays.toString(arr));
        }

    }
说明:
  • 基数排序是对传统桶排序的扩展,速度很快。
  • 基数排序是经典的时间换空间的方式,占用内存很大,对海量数据进行排序时,容易造成OutOfMemoryError
  • 基数排序是稳定的【注:假定在待排序的记录序列中,存在多个具有相同关键字的记录。若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] 等于 r[j],且 i 在 j 之前,而在排序后的序列中,r[i] 仍在r[j]之前,则称这种排序算法是稳定的,否则称为不稳定的
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!