✅ 基础版:适用于正整数,使用 10 个桶(0~9),按位分配再收集。
✅ 高阶版:支持负数和正数,自动处理范围,并优化为计数方式(类似计数排序思想),避免频繁扩容。
🌟 基数排序简介
时间复杂度:O(d × n),d 是最大数的位数,n 是元素个数
空间复杂度:O(n + k),k 是基数(通常是 10)
稳定性:稳定排序
适用场景:整数排序,尤其是位数不多的大批量数据
✅ 一、基础版基数排序(仅支持非负整数)
import java.util.ArrayList;
import java.util.List;
public class RadixSortBasic {
public static void radixSort(int[] arr) {
if (arr == null || arr.length <= 1) return;
// 找出最大值,确定最大位数
int max = getMax(arr);
// 从个位开始,对每一位进行排序
for (int exp = 1; max / exp > 0; exp *= 10) {
countingByDigit(arr, exp);
}
}
private static int getMax(int[] arr) {
int max = arr[0];
for (int value : arr) {
if (value > max) max = value;
}
return max;
}
// 按某一位(exp = 1, 10, 100...)进行“桶”分配(使用 List 桶)
private static void countingByDigit(int[] arr, int exp) {
@SuppressWarnings("unchecked")
List<Integer>[] buckets = new ArrayList[10];
for (int i = 0; i < 10; i++) {
buckets[i] = new ArrayList<>();
}
// 分配:将每个数放入对应位的桶中
for (int value : arr) {
int digit = (value / exp) % 10;
buckets[digit].add(value);
}
// 收集:按顺序从桶中取出放回原数组
int index = 0;
for (List<Integer> bucket : buckets) {
for (int value : bucket) {
arr[index++] = value;
}
}
}
// 测试
public static void main(String[] args) {
int[] arr = {170, 45, 75, 90, 2, 802, 24, 66};
System.out.println("排序前: " + java.util.Arrays.toString(arr));
radixSort(arr);
System.out.println("排序后: " + java.util.Arrays.toString(arr));
}
}
🔍 特点:
使用 List[10] 作为桶
简单易懂,适合学习
但频繁 add 和 get,性能一般
✅ 二、高阶版基数排序(支持负数 + 正数,优化性能)
💡 核心思路:
将负数和正数分开处理
负数取绝对值后逆序排列(因为 -5 < -3,但 5 > 3 )
或统一偏移成非负数(推荐)
我们采用 统一偏移法:将所有数平移到非负范围
public class RadixSortAdvanced {
public static void radixSort(int[] arr) {
if (arr == null || arr.length <= 1) return;
// 找最小值,用于偏移
int min = arr[0], max = arr[0];
for (int value : arr) {
if (value < min) min = value;
if (value > max) max = value;
}
// 偏移量:让所有数变成 >= 0
int offset = -min;
int range = max - min + 1; // 偏移后的最大值范围
// 先偏移所有数
for (int i = 0; i < arr.length; i++) {
arr[i] += offset;
}
// 对偏移后的数组进行基数排序
for (int exp = 1; range / exp > 0; exp *= 10) {
countSortByDigit(arr, exp);
}
// 恢复:减去偏移量
for (int i = 0; i < arr.length; i++) {
arr[i] -= offset;
}
}
// 计数排序式按位排序(避免使用 List,提高效率)
private static void countSortByDigit(int[] arr, int exp) {
int n = arr.length;
int[] output = new int[n]; // 输出数组
int[] count = new int[10]; // 计数数组:0~9
// 统计每个“位”上数字出现次数
for (int value : arr) {
int digit = (value / exp) % 10;
count[digit]++;
}
// 累加 count 数组(得到每个数字在 output 中的位置)
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 从后往前遍历原数组,保证稳定性
for (int i = n - 1; i >= 0; i--) {
int digit = (arr[i] / exp) % 10;
output[count[digit] - 1] = arr[i];
count[digit]--;
}
// 复制回原数组
System.arraycopy(output, 0, arr, 0, n);
}
// 测试
public static void main(String[] args) {
int[] arr = {-5, 170, -45, 75, 0, 90, -2, 802, 24, -66};
System.out.println("排序前: " + java.util.Arrays.toString(arr));
radixSort(arr);
System.out.println("排序后: " + java.util.Arrays.toString(arr));
}
}
✅ 输出示例:
排序前: [-5, 170, -45, 75, 0, 90, -2, 802, 24, -66]
排序后: [-66, -45, -5, -2, 0, 24, 75, 90, 170, 802]
✅ 高阶版优点总结
特性 说明
✅ 支持负数 通过偏移转换为非负数
✅ 性能更高 使用计数排序思想,避免动态扩容
✅ 稳定排序 从后往前填充,保持相等元素顺序
✅ 自动适应范围 动态计算 max 和 min
在基数排序中,“桶”的数量取决于你使用的“基数”(radix)。这里处理的是 十进制整数,所以:即:0, 1, 2, 3, 4, 5, 6, 7, 8, 9因为基数排序是按“位”来排序的:
个位、十位、百位……每一位的数字范围是 0 到 9,共 10 种可能。
所以我们需要 10 个桶,每个桶对应一个数字。
List<Integer>[] buckets = new ArrayList[10]; // 创建 10 个桶
然后根据当前处理的位数(比如个位),把数字放入对应的桶:
数字 个位 放入的桶
170 0 桶 0
45 5 桶 5
75 5 桶 5
90 0 桶 0
🧠 拓展:其他进制的桶数量
进制 基数(radix) 桶的数量 说明
十进制(0-9) 10 ✅ 10 个桶 最常见
二进制(0-1) 2 2 个桶 用于二进制位处理
十六进制(0-F) 16 16 个桶 如排序字节数据
字符串(a-z) 26 26 个桶 按字母排序
“基数排序”中的“基数”就是指 进制的基数(radix)
✅ 举个例子说明
🌰 假设你用 8进制(octal) 做基数排序
数字 0123(八进制)= 十进制 83
每一位只能是 0~7
所以你需要 8 个桶(0 到 7)
排序时按:个位(8⁰)、8位(8¹)、64位(8²)… 依次处理
// 8进制基数排序:需要 8 个桶
List<Integer>[] buckets = new ArrayList[8]; // 桶 0 ~ 7
🌰 十六进制(hexadecimal)呢?
数字 0x1A3 = 十进制 419
每一位是 0~9, A~F(即 0~15)
所以你需要 16 个桶
List<Integer>[] buckets = new ArrayList[16]; // 桶 0 ~ 15
取某一位的方法也要变:
// 十进制:取第 k 位(exp = 1, 10, 100...)
int digit = (num / exp) % 10;
// 十六进制:exp = 1, 16, 256...
int digit = (num / exp) % 16;
// 八进制:exp = 1, 8, 64...
int digit = (num / exp) % 8;
🔁 基数排序的流程(以 170, 45, 75, 90 为例)
第1轮:按 个位 排序(exp = 1)
170 → 个位是 0 → 放入桶 0
45 → 个位是 5 → 放入桶 5
75 → 个位是 5 → 放入桶 5
90 → 个位是 0 → 放入桶 0
收集后:[170, 90, 45, 75]
第2轮:按 十位 排序(exp = 10)
170 → 十位是 7 → 桶 7
90 → 十位是 9 → 桶 9
45 → 十位是 4 → 桶 4
75 → 十位是 7 → 桶 7
收集后:[45, 170, 75, 90]
第3轮:按 百位 排序(exp = 100)
45 → 百位是 0
170 → 百位是 1
75 → 百位是 0
90 → 百位是 0
收集后:[45, 75, 90, 170] ✅ 已排序
711

被折叠的 条评论
为什么被折叠?



