根据维基百科,基数排序的定义为: 基数排序英语:Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

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

举个栗子:

         待排序数列为:324,63,59,225,13

第一次排序(按个位大小排序):063,013,324,225,059

第二次排序(按十位大小排序):013,324,225,059,063

第三次排序(按百位大小排序):013,059,063,225,324

最终输出:13,59,63,225,324

从这个例子可以看出:下一次的排序不会影响上一次排序的结果,即当按照十位排序时如果十位上的数字相同,则它们的排序依然是上一次排序的结果(上述例子中的324与225),所以我们需要稳定排序。什么是稳定排序?

 稳定排序的意思是指, 待排序相同元素之间的相对前后关系,在各次排序中不会改变.比如实例中具有十位数字2的两个数字324和225, 在十位排序之前324在225之前,在十位排序之后, 324依然在225之前。稳定排序能保证,上一次的排序成果被保留,十位数的排序过程能保留个位数的排序成果,百位数的排序过程能保留十位数的排序成果.

 

代码实现上述过程时我们用数组来存放数据和排序结果:

第一次排序(个位数)


0

1

2

3

4

5

6

7

8

9




63

324

225




59




13


















第二次排序(十位数)


0

1

2

3

4

5

6

7

8

9


13

324



59

63






225



















第三次排序(百位数)


0

1

2

3

4

5

6

7

8

9

013


225

324







059










063










最终将二维数组中的元素按列读出来就是所得结果:

13,59,63,225,324


代码实现:

package acm;
/*基数排序*/
class RadxSort {
     public void RSort(int[] ary){
          int length = ary.length;
          int[][] bucket = new int[length][10];//桶用来存放按位排序的数
          int[] order = new int[10];        //记录一个桶中有多少个元素
          int time = 1;//记录排序次数,即最大数的位数
          int n = 1;
          int k = 0;
          while(time<=maxDigit(ary)){
          
              for(int i:ary){
                   int digit = (i/n)%10; //从个位依次往上每位上的数
                   bucket[order[digit]][digit] = i;
                   order[digit]++;//把桶中的个数加一 
              }
              for(int i=0;i<10;i++){
                   if(order[i]!=0){
                        for(int j=0;j<order[i];j++){
                             ary[k] = bucket[j][i];//把同一个桶中的数据全读出来,即从上往下读
                             k++;
                        }
                   }
                   order[i] = 0; //将记录个数清零,用以下一次排序
              }
              n*=10;
              k = 0;
              time++;
          }
     }
     //找出数组中最大数有多少位
     int maxDigit(int[] ary){
          int max=0;
          int n = 1;
          for(int i=0;i<ary.length;i++){
              if(max<ary[i])
                   max = ary[i];
          }
          while(max/10 != 0){
              max = max/10;
              n++;
          }
          return n;
     }
}
public class RadixSort{
     public static void main(String[] args){
          int[] ary = new int[]{87,15,2,47,456,987,4561,5};
          RadxSort RS = new RadxSort();
          RS.RSort(ary);
          for(int num:ary){
              System.out.print(num+",");
          }
     }   
}

算法复杂度分析

    基数排序的时间复杂度是O(kn),其中n是排序元素的个数,k是数字位数。注意这不是说这个时间复杂度一定优于O(nlog(n)),k的大小取决于数字位的选择(比如比特位数),和待排序数据所属数据类型的全集大小;k决定了进行多少轮处理,而n是每轮处理的操作数目。

以排序n个不同的整数来举例,假设这些整数以B为底,这样每位数都有B个不同的数字,K=logBN,N是待排序数据类型的全集的势。虽然有B个不同的数字,需要B个不同的桶,但在每一轮的处理中,判断每个待排序数据项只需要一次计算确定对应数位的值,因此在每一轮处理的时候都需要平均n次操作来把整数放到合适的桶中去,所以有:

  • K≈logBN

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

  • T≈logB(N)n

其中前一项是一个与输入数据无关的常数,当然该项不一定小于logn

    如果考虑和比较排序进行对照,基数排序的形式复杂度虽然不一定更小,但由于不进行比较,因此其基本操作的代价较小,而且在适当选择的B之下,k一般不大于logn,所以基数排序一般要快过基于比较的排序,比如快速排序。