基数排序过程

✅ 基础版:适用于正整数,使用 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] ✅ 已排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值