从直觉上来看,人们可能会觉得应该按最高有效位进行排序,然后再对每个盒子中的数递归地排序,最后再把结果合并起来。不幸的是,为排序每一个盒子中的数,10个盒子中的9个必须先放在一遍,这个过程产生了许多需要加以记录的中间卡片堆。
与人们的直觉相反,基数排序是首先按最低有效位数字进行排序,以解决卡片排序问题。同样,把各堆卡片收集成一叠,其中0号盒子中的在1号盒子中的前面,后者又在2号盒子中的前面,等等。然后,对整个一叠卡片按次低有效位排序,并把结果同样地合并起来。重复这个过程,直到对所有的d位数字都进行了排序。所以,仅需要d遍就可以将一叠卡片排好序。
下图说明了基数排序作用于“一叠”共7个3位数的过程。
这里也有很重要的一点,就是基数排序的稳定性,所谓稳定性,是指当有多个相同元素的值时,排序后,他们的相对次序不变。参考计数排序
想想很好理解,因为操作员把卡片从盒子里拿出来时不能改变它们的次序,否则不是乱套了吗,即使数字相同,所属者也可能是不同的。
基数排序要保持稳定,那在对每一位进行排序的时候就必须使用一个稳定的算法,这里我们就可以使用计数排序作为这个子算法。参考计数排序
基数排序的代码是很直观的。
/**
* 基数排序
* @param A 需要排序的数组,返回时已经排序
* @param d 最高位,最低位是1
* @return 结果数组
*/
public int[] RadixSort(int[] A,int d){
int[] B = A.clone();
for(int i = 1; i <= d; i++) {
//调用某一稳定排序对A数组进行排序(基于i位)
CountingSort2(A, B, i);
A = B.clone();
}
return A;
}
拆分成计数排序后就很清楚了,然后把计数排序的方法稍微修改一下
/**
* 计数排序的变种,根据某一位进行排序,k不需要,因为每一位数字在0到9之间。
* @param A 原数组
* @param B 结果数组
* @param index 基于的位数
*/
public void CountingSort2(int[] A, int[] B, int index) {
int[] C = new int[10];
for (int i : A) {
C[getDigit(A[i], index)]++;
}
for (int i = 1; i < 10; i++) {
C[i] += C[i -1];
}
for (int i = A.length -1; i >= 0; i--) {
B[C[getDigit(A[i], index)] - 1] = A[i];
C[getDigit(A[i], index)]--;
}
}
要获得数字的某位,当然直接写个方法了
/**
* 获得数字的某位
* @param digit 数字
* @param i 1-d位
* @return 单位数字
*/
public int getDigit(int digit, int i) {
int result = 0;
for(int j = 0; j < i; j++) {
result = digit % 10;
digit /= 10;
}
return result;
}
上面有所有方法的单元测试:https://github.com/qjkobe/IntroductionToAlgorithms
原文:http://blog.csdn.net/qj30212/article/details/52562387
我略微修改了一下代码和描述