基本思想
基数排序是将整数按位数分割成不同的数字,然后按每个位数分别比较,得到一个有序的序列。具体做法则是将一个数组中的每一个数统一成相同位数,位数不足高位补零。从个位开始按大小排序,当遇到位数大小相同时,要保证顺序与原数组相同。这样从个位一直到高位,就得到一个有序的序列。
实现思路
文字性的语言总是那么不便于理解,那么我们来通过图文说明下基数排序的思路。
假设有这样一个大小为10的数组:{6,68,165,4,136,98,186,82,56,47},将不足的数高位补零后,分别按照个、十、百位对数组进行排序。
可以发现,在对最高位进行排序后,该数组已然是有序的了。
代码实现
知道了基数排序的原理后就需要通过代码来实现,接下来以个位为例,说下对于个位是如何使用代码达到一轮排序效果的。
首先我们需要一个临时数组output,来存储对个位进行排序后的数组,其次还需要一个buckets数组,这个数组是基数排序的关键。
要对个位进行排序,我们先获得原数组的所有数的个位出现的次数,并保存在buckets中,在进行一次for循环后,buckets的值由全零变成了{0,0,1,0,1,1,4,1,2,0},结合数组下标可以知道,个位为2的数出现了1次,个位为6的数出现了4次。
获得完每个数个位出现的次数之后,就可以把对应的数填充到临时数组中了吗?
显然还不行,在填充之前,我们需要得到每一个数在临时数组中准确的下标。于是我们可以使用buckets[i] += buckets[i-1]
的方式获得某一位在临时数组中最右边的下标。什么意思呢?就是现在的buckets的值为{0,0,1,0,1,1,4,1,2,0},所以个位为2的数应该在临时数组的第0位,个位为4的数应该在临时数组的第2位,个位为6的有4个,那么这四个数应该分别在临时数组的第3、4、5、6位上,在执行完buckets[i] += buckets[i-1]
操作后就能获得每一位的最后一个在临时数组中最右边的位置了。
当得到上面的这个所有位数的最右边的位置后就可以将每一个数填充到临时数组里了,由于我们获得的是每一位数最右边的位置,而且需要确保相同位数大小的数的顺序与原数组中他们的相对顺序相同,所有这里需要从原数组的最后一个数开始填充。
/**
* @author: zhangocean
* @Date: 2018/11/23 19:35
* Describe: 基数排序
*/
public class RadixSort {
private static int getMax(int[] array){
int max = array[0];
for(int i=1;i<array.length;i++){
if(array[i] > max){
max = array[i];
}
}
return max;
}
/**
* @param array 数组array
* @param arrLen 数组长度
* @param exp 进行一个数某一位上的比较
*/
private static void radixSort(int[] array, int arrLen, int exp){
int[] buckets = new int[arrLen];
int[] output = new int[arrLen];
//计算一个数某位上的数出现的次数
for(int i=0;i<arrLen;i++){
buckets[(array[i]/exp)%10]++;
}
//得到每一位上的数在本轮排序中应该出现的最后位置
for(int i=1;i<arrLen;i++){
buckets[i] += buckets[i-1];
}
//将每一位上的数的最后位置与临时数组output对应
for(int i=arrLen-1;i>=0;i--){
output[buckets[(array[i]/exp)%10] - 1] = array[i];
buckets[(array[i]/exp)%10]--;
}
for(int i=0;i<arrLen;i++){
array[i] = output[i];
}
}
public static void main(String[] args) {
int[] array = {6,68,165,4,136,98,186,82,56,47};
System.out.println("排序前" + Arrays.toString(array));
int max = getMax(array);
for(int i=1;max/i > 0;i *= 10){
radixSort(array, array.length, i);
}
System.out.println("排序后" + Arrays.toString(array));
}
}
输出结果如下:
排序前[6, 68, 165, 4, 136, 98, 186, 82, 56, 47]
排序后[4, 6, 47, 56, 68, 82, 98, 136, 165, 186]
总结
基数排序向来在面试中问的较少,但是为了满足我的强迫症,凑齐这八大排序算法,还是花了点时间去理解基数排序。基数排序也是桶排序的一种扩展,基数排序也是一种稳定的排序算法。
更多了解,还请关注我的个人博客:www.zhyocean.cn