java基数排序

基数排序

(radixsort)则是属于“分配式排序”(distribution sort),基数排序法又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。

基数排序(Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机(Tabulation Machine)上的贡献[1]。

它是这样实现的: 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次排序.这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列.
基数排序的方式可以采用LSD(Least significantdigital)或MSD(Most significantdigital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

以LSD为例,假设原来有一串数值如下所示:

73,22, 93, 43, 55, 14, 28, 65, 39, 81

首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:

  0

  1 81

  2 22

  3 73 93 43

  4 14

  5 55 65

  6

  7

  8 28

  9 39

接下来将这些桶子中的数值重新串接起来,成为以下的数列:

81,22, 73, 93, 43, 14, 55, 65, 28, 39

接着再进行一次分配,这次是根据十位数来分配:

  0

  1 14

  2 22 28

  3 39

  4 43

  5 55

  6 65

  7 73

  8 81

  9 93

接下来将这些桶子中的数值重新串接起来,成为以下的数列:

14,22, 28, 39, 43, 55, 65, 73, 81, 93

这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。

LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好,MSD的方式恰与LSD相反,是由高位数为基底开始进行分配,其他的演算方式则都是相同。

效率

基数排序的时间复杂度是 O(k·n),其中n是排序元素个数,k是数字位数。注意这不是说这个时间复杂度一定优于O(n·log(n)),因为k的大小一般会受到 n 的影响。 以排序n个不同整数来举例,假定这些整数以B为底,这样每位数都有B个不同的数字,k就一定不小于logB(n)。由于有B个不同的数字,所以就需要B个不同的桶,在每一轮比较的时候都需要平均n·log2(B) 次比较来把整数放到合适的桶中去,所以就有:

  • k 大于或等于 logB(n)
  • 每一轮(平均)需要 n·log2(B) 次比较

所以,基数排序的平均时间T就是:

T ≥logB(nn·log2(B) = log2(n)·logB(2n·log2(B)= log2(n)·n·logB(2)·log2(B) = n·log2(n)

所以和比较排序相似,基数排序需要的比较次数:T ≥ n·log2(n)。故其时间复杂度为Ω(n·log2(n)) = Ω(n·log n) 。

时间效率:设待排序列为n个记录,d个关键码,关键码的取值范围为radix,则进行链式基数排序的时间复杂度为O(d(n+radix)),其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(n),共进行d趟分配和收集。 空间效率:需要2*radix个指向队列的辅助空间,以及用于静态链表的n个指针。

实现的方法

最高位优先(Most Significant Digit first)法,简称MSD法:先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。

最低位优先(Least Significant Digit first)法,简称LSD法:先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。

使用范围

基数排序从低位到高位进行,使得最后一次计数排序完成后,数组有序。

其原理在于对于待排序的数据,整体权重未知的情况下,先按权重小的因子排序,然后按权重大的因子排序。例如比较时间,先按日排序,再按月排序,最后按年排序,仅需排序三次。

但是如果先排序高位就没这么简单了。

基数排序源于老式穿孔机,排序器每次只能看到一个列,

很多教科书上的基数排序都是对数值排序,数值的大小是已知的,与老式穿孔机不同。

将数值按位拆分再排序,是无聊并自找麻烦的事。

算法的目的是找到最佳解决问题的方案,而不是把简单的事搞的更复杂。

基数排序更适合用于对时间、字符串等这些整体权值未知的数据进行排序。

这时候基数排序的思想才能体现出来,例如字符串,如果从高位(第一位)往后排就很麻烦。

而反过来,先对影响力较小,排序排重因子较小的低位(最后一位)进行排序就非常简单了。

这时候基数排序的思想就能体现出来。

又或者所有的数值都是以字符串形式存储,就象穿孔机一样,每次只能对一列进行排序。

这时候基数排序也适用,例如:对{"193";"229";"233";"215"}进行排序

下面我们使用基数排序对字符串进行排序。

对每个位循环调用计数排序。

基数排序的缺点: 

不呈现时空局部性,因为在按位对每个数进行排序的过程中,一个数的位置可能发生巨大的变化,所以不能充分利用现代机器缓存提供的优势。同时计数排序作为中间稳定排序的话,不具有原地排序的特点,当内存容量比较宝贵的时候,还是有待商榷。

 

实例说明

package com.test.sort;

 

public class RadixSort {

 

    publicstatic void sort(int[] number, int d) {

        int k= 0;

        int n= 1;

        int m= 1;

       int[][] temp = new int[number.length][number.length];

        int[]order = new int[number.length];

        while(m <= d) {

           for (int i = 0; i < number.length; i++) {

               int lsd = ((number[i] / n) % 10);

               temp[lsd][order[lsd]] = number[i];

               order[lsd]++;

            }

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

               if (order[i] != 0) for (int j = 0; j < order[i]; j++) {

                   number[k] = temp[i][j];

                   k++;

               }

                order[i] = 0;

            }

            n*= 10;

            k= 0;

           m++;

        }

    }

 

    publicstatic void main(String[] args) {

        int[]data = { 73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100 };

        for(int i = 0; i < data.length; i++) {

           System.out.print(data[i] + " ");

        }

       System.out.println("\n排序后:");

       RadixSort.sort(data, 10);

        for(int i = 0; i < data.length; i++) {

           System.out.print(data[i] + " ");

        }

    }

}

 

 

package com.test.sort;

 

 

import java.util.Arrays;

 

public class MultiKeyRadixSortTest {

 

    publicstatic void main(String[] args) {

        int[]data = new int[] { 1100, 192, 221, 12, 23, 45, 343, 4545, 4545, 1100, 192, 221,12, 23 };

       print(data);

        // 4:最大数的位数

       radixSort(data, data.length, 4);

       System.out.println("排序后的数组:");

       print(data);

    }

 

    publicstatic void radixSort(int[] data, int radix, int d) {

        // 缓存数组

        int[]tmp = new int[data.length];

        //buckets用于记录待排序元素的信息

        //buckets数组定义了max-min个桶

        int[]buckets = new int[radix];

 

        for(int i = 0, rate = 1; i < d; i++) {

 

            //重置count数组,开始统计下一个关键字

           Arrays.fill(buckets, 0);

            //将data中的元素完全复制到tmp数组中

           System.arraycopy(data, 0, tmp, 0, data.length);

 

            //计算每个待排序数据的子关键字

           for (int j = 0; j < data.length; j++) {

               int subKey = (tmp[j] / rate) % radix;

                buckets[subKey]++;

            }

 

           for (int j = 1; j < radix; j++) {

               buckets[j] = buckets[j] + buckets[j - 1];

            }

 

            //按子关键字对指定的数据进行排序

           for (int m = data.length - 1; m >= 0; m--) {

               int subKey = (tmp[m] / rate) % radix;

               data[--buckets[subKey]] = tmp[m];

            }

           rate *= radix;

        }

 

    }

 

    publicstatic void print(int[] data) {

        for(int i = 0; i < data.length; i++) {

            System.out.print(data[i] +"\t");

        }

       System.out.println();

    }

 

}

http://baike.baidu.com/view/1170573.htm

http://zh.wikipedia.org/wiki/%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F

http://puffsun.iteye.com/blog/1294552

http://www.bioisland.com/Algorithm/ShowArticle.asp?ArticleID=44

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值