Radix Sort 基数排序是对计数排序的改进,该算法可以支持针对浮点、字符串等类型元素进行排序。其主要思想是将排序元素按数位分割依次排序,从而实现整体有序。其同样可以具有线性时间的性能
基本原理
在对非负整数进行基数排序时,需要首先将排序元素统一为相同的数位长度,数位不足的排序元素可以添加前导零的方式实现数位长度统一对齐;然后从排序元素的最低位(个位)开始进行排序,直到完成对最高位的排序,此时即实现了对排序元素的整体排序。而对每个数位进行排序的过程则是通过(稳定的)计数排序完成的,故基数排序同样是稳定的。由于我们是从排序元素的最低位向最高位依次排序的,故这种方式被称为最低位(LSD,Least Significant Digit)法;反之,如果是从排序元素的最高位向最低位依次排序的,则被称之为最高位(MSD,Most Significant Digit)法
实际上,基数排序算法对排序元素的类型不要求一定是非负整数才可以进行,其对于字符串、浮点数等类型均可适用。其关键在于要求排序元素的数位长度统一。例如对整数排序时,如果排序元素中含有负数,则可以对排序元素均加上一个数使其全部为非负整数;如果元素类型是字符串的话,在计数排序过程中,可以直接使用该位字符对应ASCII码值进行计数,对于长度不足的字符串,可直接在其后面补0实现长度对齐。即在计数排序过程中,如果发现某位字符是为对齐所填充的0的话,则可认为其对应的ASCII码值为0进行计数,因为字符’A’所对应的ASCII码值是65,字符’0’所对应的ASCII码值是48,均比0大。这样即可保证基数排序的结果是符合字典序的
实现
这里是一个通过Java实现的对非负整数类型的元素进行基数排序的实例,来帮助大家更好地理解该算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
|
public class RadixSort {
public static void sort() { int[] array = getTestCase(); System.out.println("before sort: " + Arrays.toString(array) ); array = radixSortByLSD(array); System.out.println("after sort: " + Arrays.toString(array) ); }
private static int[] radixSortByLSD(int[] array) { int radix = 10; int size = array.length; int[] sortedArray = new int[size];
int maxDigits = -1; for(int element : array ) { maxDigits = Math.max( getDigits(element), maxDigits); }
for(int i=1; i<=maxDigits; i++) { int[] counts = new int[radix]; for(int element : array) { int num = getNumberByIndex(element, i); counts[num]++; } for(int j=1; j<radix; j++) { counts[j] = counts[j-1] + counts[j]; } for(int j=size-1; j>=0; j--) { int num = getNumberByIndex(array[j], i); int sortIndex = counts[num]-1; sortedArray[sortIndex] = array[j]; counts[num]--; } array = sortedArray.clone(); } return array; }
private static int getDigits(int num) { if(num==0) { return 1; } int digits = (int)Math.log10(num) + 1; return digits; }
private static int getNumberByIndex(int num, int index) { int digits = getDigits(num); if(index>digits) { return 0; } int numInIndex = num / (int)Math.pow(10, index-1) % 10; return numInIndex; }
private static int[] getTestCase() { int[] caseArray = {220, 134, 0, 153, 2, 87}; return caseArray; } }
|
测试结果如下:
特性
N为排序元素的数目,r为基数,d为排序元素的最大位数(长度)。当d为常数且r=O(N)时,其具有线性时间复杂度
参考文献
- 算法导论 · 第3版
- 计算机程序设计艺术(第3卷):排序与查找 高德纳(Donald E.Knuth) 著