分配排序之基数排序
基数排序是在计数排序上的应用扩展,是分配排序的一个特例,我们已经知道计数排序(桶排序)适用于小数范围内的排序,对于大数则效率不高,而今天要说的基数排序,利用了进制基数的特点,将大数拆解为进制内范围的小数,多次执行计数或其他稳定排序,实现了大数范围内的排序功能。
例如对于两位数之间的排序问题,我们一般从十位开始比较,十位相同的数,再比较个位,最终得出想要的结果,基数排序的过程大致如此,不同之处在于对于十位或个位相同的数,我们采用计数排序的处理,中间不需要进行元素之间的比较。
基数排序分为高位优先法(most significant digit first, MSD)与低位优先法(least significant digit first, LSD),计算机在实现高位优先法时,需要进行嵌套分配桶空间,是一个递归分治的算法,执行起来比较复杂,因此LSD的实现则更为常见。
LSD基于数组的实现代码如下:
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int[] arr = new int[] { 97, 53, 88, 59, 26, 41, 88, 31, 22 };
arr = radixSort(arr, 9, 2, 10);
System.out.println(Arrays.toString(arr));
}
/**
*
* @param array 排序序列
* @param n 排序序列元素数
* @param d 排序位数(两位数传入2)
* @param r 基
*/
public static int[] radixSort(int[] array, int n, int d, int r) {
// 内部桶排序时用以保存各桶中的元素数
int[] count = new int[r];
// 按照不同基位进行桶排序时存放元素
int[] tmpArray = new int[n];
// 模除数,计算桶位
int radix = 1;
// 临时变量,用于计算每个元素的桶位,避免重复声明
int k = 0;
// 进行d轮计数排序(/桶排序)
for (int i = 0; i < d; i++) {
// 每轮循环,须重新初始化count数组
for (int j = 0; j < r; j++) {
count[j] = 0;
}
// 遍历原始数组,计算每个桶位的元素数,存储于count数组
for (int j = 0; j < n; j++) {
k = (array[j] / radix) % r;
count[k]++;
}
// 计数器对应位数值修改为下一位的开始位置
for (int j = 1; j < r; j++) {
count[j] = count[j] + count[j - 1];
}
// 进行桶排序过程
for (int j = n - 1; j >= 0; j--) {
// 计算元素桶位
k = (array[j] / radix) % r;
count[k]--;
tmpArray[count[k]] = array[j];
}
// 回写到原数组
for (int j = 0; j < n; j++) {
array[j] = tmpArray[j];
}
// 基位递增
radix *= r;
}
return array;
}
}
小结
基数排序的主要思想是按照基数对排序元素进行拆分,拆分后的每个值都属于进制内的数值,在此基础上,按照每一位进行桶排序,与直观的理解不同,由于计算机在处理递归分治算法时较为复杂,因此一般实现方式是LSD–低位优先的算法,稍微考虑下,高位优先MSD与低位LSD之间的直观区别仅仅是排序时处理的方向不一样。
推荐资料:
Radix sort - Wikiwand
欢迎关注我的公众号,了解更多内容: