java 实现 常见排序算法(四)基数排序

大家好,我是烤鸭:    

   今天分享一下基础排序算法之基数排序。

1.    基数排序:


原理:基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort。

将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

思路:

基数排序,即一个数位一个数位地进行排序,平常生活中我们经常使用的一种算法思想:如要对一个日期进行排序,日期中由年、月、日组成的,对于这个问题,我们经常使用的是先比较年份,如果相同再比较月份,如果还相同就比较日。

下面四个数进行排序:123、312、245、531

   个位 =>  十位 =>  百位

   531      312      123

   312      123      245

   123      531      312

   245      245      531

代码实现:

 /**
     * 基数排序
     * @param d
     * @param array
     *  时间复杂度 O的log2 N
     *  基数排序 运用二维数组来分别比较每一位,个位、十位、百位…
     * 输入10个整数的数组
     */
    private void radixSort(int d,int[] array){
        long nowTime = System.nanoTime();
        int n=1;//代表位数对应的数:1,10,100000...
        int k=0;//保存每一位排序后的结果用于下一位的排序输入
        int[][] bucket=new int[10][array.length];//排序桶用于保存每次排序后的结果,这一位上排序结果相同的数字放在同一个桶里
        int[] num=new int[array.length];//用于保存每个桶里有多少个数字 ,最多为输入数组长度
        while(n<=d)
        {
            for(int e:array) //将数组array里的每个数字放在相应的桶里
            {
                int digit=(e/n)%10;
                bucket[digit][num[digit]]=e;
                num[digit]++;
            }
            for(int i=0;i<array.length;i++)//将前一个循环生成的桶里的数据覆盖到原数组中用于保存这一位的排序结果
            {
                if(num[i]!=0)//这个桶里有数据,从上到下遍历这个桶并将数据保存到原数组中
                {
                    for(int j=0;j<num[i];j++)
                    {
                        array[k]=bucket[i][j];
                        k++;
                    }
                }
                num[i]=0;//将桶里计数器置0,用于下一次位排序
            }
            n*=10;
            k=0;//将k置0,用于下一轮保存位排序结果
        }
        System.out.println("基数排序,花费时间(ms):" + ((System.nanoTime() - nowTime) / 1000000.0) + "ms");
    }

基数排序是稳定算法,效率很高,其复杂度为O(nlog(r)m),其中r为所采取的基数,而m为堆数。但它只能用在整数的排序中,且需要借助一定的辅助空间。

上面的代码适用有些局限在于对基数值选取:

int [] x = {25 ,11 ,22 ,34 ,15 ,44 ,76, 66, 100, 8 ,14, 20 ,2, 5 ,1 };

比如这个数组,基数应该选3。附上一个选择基数值的方法。

 /**
     * 获取基数排序中的基数
     * @param array
     * @return
     */
    public int getRadixBasicNumber(int[] array){
        if(array == null && array.length ==0) {
            return 0;
        }
        int max =0;
        //1获取最大的绝对值的数
        for(int i =0;i<array.length;i++) {
            if(Math.abs(max)<Math.abs(array[i])) {
                max = array[i];
            }
        }
        int times = 0;
        if(max<0) max = -max;
        //2求出最大的绝对值的数,是10的times次幂。
        while(max >0) {
            max = max/10;
            times ++;
        }
        return times;
    }

耗时对比:

10W 条随机 数据 运行如图:

分别对比了基数排序,直插排序,希尔排序和快速排序。差距很明显。

50W 条随机 数据 运行如图:

分别对比了基数排序,直插排序,希尔排序和快速排序。差距很明显。

100W 条随机 数据 运行如图:

分别对比了基数排序,直插排序,希尔排序和快速排序。差距很明显。

总结:

基数排序

优点:效率高。

缺点:占用内存大,只能对正整数排序(采用二维数组做桶时)

最坏时间复杂度:O(P(N+B))。

平均时间复杂度为:O(P(N+B))。

其中P是排序的趟数,N是要被排序元素的个数,B是桶数。

各种排序方法比较:

更多排序算法:

冒泡排序   :  https://blog.csdn.net/Angry_Mills/article/details/81057900

插入排序   :  https://blog.csdn.net/Angry_Mills/article/details/81208700

快速排序   :  https://blog.csdn.net/Angry_Mills/article/details/83339390

发布了115 篇原创文章 · 获赞 58 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/Angry_Mills/article/details/83383572