09 基数排序

0

基数排序(Radix Sort)是桶排序的扩展,它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。
排序过程:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

算法描述

  1. 取得数组中的最大数,并取得位数;
  2. arr为原始数组,从最低位开始取每个位组成radix数组;
  3. 对radix进行计数排序(利用计数排序适用于小范围数的特点);

原理

  • 根据待排序列元素的大小范围,均匀独立的划分M个桶
  • 将N个输入元素分布到各个桶中去
  • 再对各个桶中的元素进行排序
  • 此时再按次序把各桶中的元素列出来即是已排序好的。

性能

时间复杂度为O(N+C),O(C)=O(M(N/M)log(N/M))=O(NlogN-NlogM),空间复杂度为O(N+M),算法是稳定的,且与初始序列无关。

使用场景

算法思想和散列中的开散列法差不多,当冲突时放入同一个桶中;可应用于数据量分布比较均匀,或比较侧重于区间数量时。

代码实现:

public abstract class Sorter {
     public abstract void sort(int[] array);
}

public class RadixSorter extends Sorter {

     private int radix;

     public RadixSorter() {
          radix = 10;
     }

     @Override
     public void sort(int[] array) {
          // 数组的第一维表示可能的余数0-radix,第二维表示array中的等于该余数的元素
          // 如:十进制123的个位为3,则bucket[3][] = {123}
          int[][] bucket = new int[radix][array.length];
          int distance = getDistance(array); // 表示最大的数有多少位
          int temp = 1;
          int round = 1; // 控制键值排序依据在哪一位
          while (round <= distance) {
               // 用来计数:数组counter[i]用来表示该位是i的数的个数
               int[] counter = new int[radix];
               // 将array中元素分布填充到bucket中,并进行计数
               for (int i = 0; i < array.length; i++) {
                    int which = (array[i] / temp) % radix;
                    bucket[which][counter[which]] = array[i];
                    counter[which]++;
               }
               int index = 0;
               // 根据bucket中收集到的array中的元素,根据统计计数,在array中重新排列
               for (int i = 0; i < radix; i++) {
                    if (counter[i] != 0)
                         for (int j = 0; j < counter[i]; j++) {
                              array[index] = bucket[i][j];
                              index++;
                         }
                    counter[i] = 0;
               }
               temp *= radix;
               round++;
          }
     }

     private int getDistance(int[] array) {
          int max = computeMax(array);
          int digits = 0;
          int temp = max / radix;
          while(temp != 0) {
               digits++;
               temp = temp / radix;
          }
          return digits + 1;
     }

     private int computeMax(int[] array) {
          int max = array[0];
          for(int i=1; i<array.length; i++) {
               if(array[i]>max) {
                    max = array[i];
               }
          }
          return max;
     }
}