基数排序 (Radix Sort)

算法原理
———————————————————————————————————
基数排序 (Radix Sort) 是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。
————
排序过程:将所有待比较数值(必须是正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序;然后从桶中倒出所有数,完成一次排序;依次迭代到高位,这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
————
基数排序法会使用到桶 (Bucket),顾名思义,通过将要比较的位(个位、十位、百位…),将要排序的元素分配至 0~9 个桶中,借以达到排序的作用,在某些时候,基数排序法的效率高于其它的比较性排序法。
————
注:基数排序要满足的条件<1>必须是正整数;<2>从低位开始判断,一直迭代到最高位;<3>采用稳定排序。

多关键字排序时有两种解决方案:
最高位优先法(MSD)(Most Significant Digit first)
最低位优先法(LSD)(Least Significant Digit first)
例如,对如下数据序列进行排序。
192,221,12,23
可以观察到它的每个数据至多只有3位,因此可以将每个数据拆分成3个关键字:百位(高位)、十位、个位(低位)。
如果按照习惯思维,会先比较百位,百位大的数据大,百位相同的再比较十位,十位大的数据大;最后再比较个位。人得习惯思维是最高位优先方式。
如果按照人得思维方式,计算机实现起来有一定的困难,当开始比较十位时,程序还需要判断它们的百位是否相同–这就认为地增加了难度,计算机通常会选择最低位优先法。
基数排序方法对任一子关键字排序时必须借助于另一种排序方法,而且这种排序方法必须是稳定的。
对于多关键字拆分出来的子关键字,它们一定位于0-9这个可枚举的范围内,这个范围不大,因此用桶式排序效率非常好。
对于多关键字排序来说,程序将待排数据拆分成多个子关键字后,对子关键字排序既可以使用桶式排序,也可以使用任何一种稳定的排序方法。

此处给出两种实现方法:
<1>采用计数排序来实现基数排序,上一篇中有去实现过计数排序的实现,个人感觉计数排序所选择的桶有可能太多,因为是对于待排序中的最大值和最小值的差距个桶,基数排序所选的桶则为0-9号桶,对于每一位都采用计数排序,则能排好每一位的数,然后利用基数排序从低位到高位的迭代就可以完成排序。从时间和空间上都有所提升。

package 排序;

import java.util.Arrays;

public class RadixSort {


    //基于计数排序的基数排序算法 
    private static void radixSort(int[] array,int radix, int distance) {  
        //array为待排序数组 
        //radix,代表基数 
        //代表排序元素的位数 

        int length = array.length;  
        int[] temp = new int[length];//用于暂存元素 
        int[] count = new int[radix];//用于计数排序 
        int divide = 1;  

        for (int i = 0; i < distance; i++) {  

            System.arraycopy(array, 0,temp, 0, length);  
            Arrays.fill(count, 0);  

            for (int j = 0; j < length; j++) {  
                int tempKey = (temp[j]/divide)%radix;  
                count[tempKey]++;  
            }  

            for (int j = 1; j < radix; j++) {  
                count [j] = count[j] + count[j-1];  
            }  

            //运用计数排序实现基数排序的重点在下面这个方法 
            for (int j = length - 1; j >= 0; j--) {  
                 //这个必须从后面算,因为count统计的是总数
                int tempKey = (temp[j]/divide)%radix;  
                count[tempKey]--;  
                array[count[tempKey]] = temp[j];  
            }  

            divide = divide * radix;                  

        }  

    }  


    /** * @param args */  
    public static void main(String[] args) {  

        int[] array = {3,2,3,2,5,333,45566,2345678,78,990,12,432,56};  
        radixSort(array,10,7);  
        for (int i = 0; i < array.length; i++) {  
            System.out.print(" " + array[i]);  
        }  


    }  


}

<2>采用java提供的数据结构Arraylist和LinkedList来实现,具体实现过程中有解释

public static void radixSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int negNum = 0; 
        for (int i = 0; i < arr.length; i++) {
            //统计正负整数的个数
            negNum += arr[i] < 0 ? 1 : 0;
        }
        int[] negArr = new int[negNum];
        int[] posArr = new int[arr.length - negNum];
        int negi = 0;
        int posi = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] < 0) {
                negArr[negi++] = -arr[i];
            } else {
                posArr[posi++] = arr[i];
            }
        }
        radixSortForPositive(negArr);
        radixSortForPositive(posArr);
        int index = 0;
        for (int i = negArr.length - 1; i >= 0; i--) {
            arr[index++] = -negArr[i];
        }
        for (int i = 0; i < posArr.length; i++) {
            arr[index++] = posArr[i];
        }
    }

    public static void radixSortForPositive(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        /*这里的数据结构是,ArrayList中存放的是LinkedList,原因是对于每个桶中的元素要进行调整,所以采用LinkedList; 而对于桶与桶之间只需要进行访问操作,所以选取ArrayList。 对于需要快速插入,删除元素,应该使用LinkedList。 对于需要快速随机访问元素,应该使用ArrayList。*/
        ArrayList<LinkedList<Integer>> qArr1 = new ArrayList<LinkedList<Integer>>();
        ArrayList<LinkedList<Integer>> qArr2 = new ArrayList<LinkedList<Integer>>();
        for (int i = 0; i < 10; i++) {
            qArr1.add(new LinkedList<Integer>());
            qArr2.add(new LinkedList<Integer>());
        }
        for (int i = 0; i < arr.length; i++) {
            //arr[i] % 10,得到对应的位上的数,get()找到对应的桶,offer()添加到对应的桶
            qArr1.get(arr[i] % 10).offer(arr[i]);
        }
        long base = 10;
        while (base <= Integer.MAX_VALUE) {
            for (int i = 0; i < 10; i++) {
                //get()找到对应的桶
                LinkedList<Integer> queue = qArr1.get(i);
                while (!queue.isEmpty()) {
                    //倒桶
                    int value = queue.poll();
                    //按从低位到高位迭代
                    qArr2.get((int) (value / base) % 10).offer(value);
                }
            }
            ArrayList<LinkedList<Integer>> tmp = qArr1;
            qArr1 = qArr2;
            qArr2 = tmp;
            base *= 10;
        }
        int index = 0;
        for (int i = 0; i < 10; i++) {
            LinkedList<Integer> queue = qArr1.get(i);
            while (!queue.isEmpty()) {
                arr[index++] = queue.poll();
            }
        }
    }

你可能感兴趣的:(算法,排序算法)