参考文章:https://blog.youkuaiyun.com/ns_code/article/details/20478753
基数排序
基数排序的排序时间也可以达到线性,尤其在k和d(后面介绍该参数)很小的情况下。
基数排序采取的是多关键字比较的策略,且每个关键字对排序的影响不同,根据关键字影响的主次,有两种排序方法
:
1、先根据影响最大的关键字
来排序,而后在该关键字相同的情况下,再根据影响次之的关键字来排序,依此类推,直到最后按照影响最小的关键字排序后,序列有序。我们称这个为先高位后低位。
2、先根据影响最小的关键字
来排序,而后再对全部的元素根据影响次小的关键字来排序,依此类推,直到最后按照影响最大的关键字排序后,序列有序。我们称这个为先低位后高位。
这有点抽象,我们用具体的例子来说明。比如,我们希望用三个关键字(年、月、日)来对日期进行排序,按照基数排序的思想,则:
采取第一种方法排序的思路是这样的:先比较年,形成一个按照年有序排列的序列,而后对年相等的日期,在比较月,对月相等的日期,再比较日,最后得到有序序列。
采取第二种方法排序的思路是这样的:先对所有元素按照日排序,再对所有元素按照月排序,最后对所有元素按照年排序,得到有序序列。
我们一般采用第二种方法来进行排序,比如如下的数字序列:
217,125,362,136,733,522
先对个位上的数字进行排序,得到:
362,522,733,125,136,217
再对十位上的数字进行排序,得到:
217,522,125,733,136,362
最后对百位上的数字进行排序,得到:
125,136,217,362,522,733
很明显,高位数字比低位数字对排序的影响大,该方法的正确性很容易证明,这里不再说明。
我们注意到,这里每一步都需要对各个位上的数进行排序。而为了保证基数排序的正确性(稳定性)
,我们对每个位上的数进行排序时可以选用计数排序
。算法导论上给的伪代码太粗略了,直接贴出自己写的完全代码(包括测试代码),如下:
package com.zhumq.sort;
import java.util.Arrays;
import org.junit.Test;
public class BaseSort {
/*
* 这里每一步都需要对各个位上的数进行排序。而为了保证基数排序的正确性(稳定性),
* 我们对每个位上的数进行排序时可以选用计数排序
*/
/*
* 在第一种计数排序的实现形式上做了些修改
计数排序后的顺序为从小到大
arr[0...len-1]为待排数组,我们这里采用三位数
brr[0...len-1]为排序后的有序数组
w[0...len-1]用来保存取出的每一位上的数,其每个元素均是0-k中的一个值
crr[0...k]保存0...k中每个值出现的次数
*/
//主要是新增w[]来保存取出的每一位上的数
public void count_sort(int arr[],int brr[],int w[],int crr[],int len ,int k) {
int i;
//数组crr各元素置0,这步是必须的,因为一位计数排序结束后,crr里面是有值的,如果不重新置零,下面放置操作会出现数组越界!
for(i=0;i<=k;i++) {
crr[i] = 0;
}
//统计数组w中每个元素重复出现的次数
for(i = 0;i<len;i++) {
crr[w[i]]++;
}
//求数组w中小于等于i的元素个数
for(i=1;i<=k;i++) {
crr[i] += crr[i-1];
}
//把arr中的元素放在brr中对应的位置上
for(i=len-1;i>=0;i--) {
//跟前面计数排序一样
brr[crr[w[i]]-1] = arr[i];
crr[w[i]]--;
}
//再将brr中的元素依次复制给arr,arr也就有序了
for(i=0;i<len;i++) {
arr[i] = brr[i];
}
}
/*
基数排序后的顺序为从小到大
其中参数d为元素的位数
*/
public void basicsort(int arr[],int brr[],int w[],int crr[],int len,int k ,int d) {
int i , j , val = 1;
//从低位到高位依次进行计数排序
for(i = 1;i<=d;i++) {
//w[]保存的是arr中每个元素对应位上的数
//每位上值得范围在0~k之间
for(j = 0;j<len;j++) {
w[j] = (arr[j]/val)%10;
}
//对当前位进行计数排序
count_sort(arr,brr,w,crr,len,k);
val *= 10;
}
}
@Test
public void test() {
int arr[] = {217,125,362,136,733,522};
int len = arr.length;
int brr[] = new int[len];
int k = 7;
int crr[] = new int[k+1];
int w[] = new int[len];
int d = 3;
//第一次出现问题原因:crr[]未置零!
basicsort(arr, brr, w, crr, len, k, d);
System.out.println(Arrays.toString(arr));
}
}
测试结果如下:
最后我们同样对基数排序稍微做下总结:
1、同样不是基于比较的排序,因此可以达到线性排序时间;
2、同样采取空间换时间的思想,需要额外的辅助空间,但是时间复杂度仅为O(d(n+k))
;
3、基数排序的稳定性同样也很好。