定义
基数排序,是一种非比较型整数排序算法,先把所有元素补位,让所有元素的位数相同,然后把序列中的每个元素按照位数进行分桶的一种算法,基数排序的实现方法分为两种:MSD和LSD。
- MSD:最高位优先法(Most Significant Digit First),先比较最高位,最高位分到一个桶中的,再按照第二位进行分桶…,知道分到最后一位,然后再从最小的桶中逐层向上,把元素都拿出来,即完成排序。
- LSD:最低位优先法(Least Significant Digit First),先比较最低位,也就是个位,进行分桶,分桶过程中分到一个桶中的数据直接追加到桶中即可,无需排序。然后将所有同种的元素按桶的顺序拿出,重新组成序列,然后比较十位,进行分桶…直到比较到最高位,重新组成序列即可完成排序。
- 稳定性:根据 相等元素 在数组中的 相对顺序 是否被改变,排序算法可分为「稳定排序」和「非稳定排序」两类。
- 就地性:根据排序过程中 是否使用额外内存(辅助数组),排序算法可分为「原地排序」和「异地排序」两类。一般地,由于不使用外部内存,原地排序相比非原地排序的执行效率更高。
- 自适应性:根据算法 时间复杂度 是否 受待排序数组的元素分布影响 ,排序算法可分为「自适应排序」和「非自适应排序」两类。「自适应排序」的时间复杂度受元素分布影响,反之不受其影响。
- 比较类:比较类排序基于元素之间的 比较算子(小于、相等、大于)来决定元素的相对顺序;相对的,非比较排序则不基于比较算子实现。
图解
- 以LSD为例
- 设定 10 个桶,分别存放位数为 0-9 的元素
- 遍历初始序列,将元素放入不同的桶中
- 按照桶的顺序,把桶中元素全部拿出来重新组装成一个序列
- 重复2、3步骤,直到最高位。即可完成排序
性质
- 时间复杂度
- 最佳 O(nk)
- 平均 O(nk)
- 最差 O(nk)
- 空间复杂度
- 最差 O(n+k)
- 稳定性:稳定
- 就地性:非原地
- 自适应性:非自适应
- 比较类:非比较
Java
public class RadixSort {
public static void radixSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
// 采用LSD方法
radixSortLSD(arr, 1);
}
/**
* LSD方式 排序
* @param arr 序列
* @param digit 位数 个位1十位2百位3......
*/
private static void radixSortLSD(int[] arr, int digit) {
int len = arr.length;
// 定义十个桶(分别存放个位数为0、1、2......9的数字)
int[][] buckets = new int[10][];
boolean isAppend = false;
for (int i = 0; i < len; i++) {
String valStr = String.valueOf(arr[i]);
if (valStr.length() >= digit) {
int index = Integer.parseInt(valStr.substring(valStr.length() - digit, valStr.length() - digit + 1));
buckets[index] = appendItem(buckets[index], arr[i]);
isAppend = true;
}
}
// 从桶中拿出所有元素
if (isAppend) {
int k = 0;
for (int[] b : buckets) {
if (b != null) {
for (int i = 0; i < b.length; i++) {
arr[k++] = b[i];
}
}
}
digit++;
radixSortLSD(arr, digit);
}
}
private static int[] appendItem(int[] bucketArr, int val) {
if (bucketArr == null || bucketArr.length == 0) {
return new int[]{val};
}
// 拷贝一下原来桶的序列,并增加一位
int[] arr = Arrays.copyOf(bucketArr, bucketArr.length + 1);
arr[bucketArr.length] = val;
return arr;
}
}