基数排序
学习目标:掌握基数排序算法的原理和思想
一、前提知识
排序算法概念、时间复杂度。可前往此网址 排序算法学习01_算法基础介绍阅读
二、基数排序介绍
基数排序( Radix sort ),基数排序算法通过对原序列中元素的各个位数分别进行切割来排序,可以说是我们前两篇计数排序和桶排序的结合版本,也是一种非比较型的整数排序算法。且稳定的。
三、基数排序工作原理
基数排序也如计数排序一样,利用数组下标排序元素,但此时它并不是直接用元素大小来对应数组下标,而是通过对元素值的每个位数进行切割再来对应数组下标放置元素。
那么显然此时一个数组是远远不够的,一个索引位只能放置一个元素,所以基数排序又结合了桶排序的设计逻辑,这样每个桶(下标)位就可以放置多个元素
基数排序通过切割整数位数,每次先取整数最后一位根据桶位置先进行放置元素,放置完毕依次取出放回原序列,然后依次向前递进一位再放置,重复该逻辑,直到 所有最高位放置完毕 即可得到一个排序好的序列
图解:
四、基数排序设计思路
基数排序算法设计,较于我们上篇学的前两种排序是比较简单的,只需要得到原序列最大值的长度,就可以得到要排序的次数,然后设计一个公式,每次对值进行入桶时都能根据该公式进入对应的桶
- 得到最大值长度,我们可以使用JDK内置的String类提供的valueOf方法对数值进行转换成String类型,然后取长度
- 我们知道,值入桶是根据,值的位数进行的。那么获取每个值的位数相信小伙伴们都学过
- 首先对个位进行处理,那么如果存在元素123,那获取个位 = 123 / 1 % 10
- 百位 = 123 / 10 % 10 千位 = 123 / 100 % 10
- 。。。得到该规律,我们就可以进行实现了
- 还有,对于桶的选择,依然采用上篇桶排序的思路,用ArrayList
- 那么接下来看代码吧
五、代码实现
public static void radixSort(int[] arr){
int max = arr[0]; // 最大值
for(int i = 1;i < arr.length;i++){
if(max < arr[i]) max = arr[i];
}
int maxLength = String.valueOf(max).length(); // 根据最大值获取长度
ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>(); // 桶列表
// 位数取值为0~9,那么创建10个桶即可
for(int i = 0;i < 10;i++){
bucketList.add(new ArrayList<>());
}
// 排序
int digit = 1; // 先表示个位
for(int i = 1;i <= maxLength;i++){
// 遍历序列入桶
for(int j = 0;j < arr.length;j++){
int index = (arr[j] / digit) % 10; // 获取该位数对应的桶位置
bucketList.get(index).add(arr[j]);
}
digit*=10; // 计算前一个位数
int arrIndex = 0; // 作为序列索引,辅助赋值
// 遍历桶写回原序列,从第一个桶开始写就是升序,反之
for(int k = 0;k < 10;k++){
if(bucketList.get(k).size() > 0) {
for (int m = 0; m < bucketList.get(k).size(); m++) {
arr[arrIndex++] = bucketList.get(k).get(m);
}
}
bucketList.get(k).clear(); // 清空
}
}
}
六、时间复杂度
基数排序是非比较排序算法,算法的时间复杂度是O(n*k). 关于更多详细请参考其他书籍或博客
七、总结
基数排序在运算性能上可能没有计数和桶排序好,但是它也弥补了计数排序和桶排序的不足,计数排序不适合排序元素相差过大的序列,而桶排序不适合排序分布不均匀的数据。