基数排序(Radix Sort)的进阶应用:对包含负数的整数数组进行排序
字数 2135 2025-12-06 05:05:06
基数排序(Radix Sort)的进阶应用:对包含负数的整数数组进行排序
题目描述
标准的基数排序(Radix Sort)通常用于对非负整数数组进行排序,其核心思想是从最低位到最高位(LSD,最低有效位优先)或从最高位到最低位(MSD,最高有效位优先)逐位进行稳定的计数排序。
本题要求扩展基数排序,使其能够正确处理包含负数的整数数组。例如,输入数组为 [170, -45, 75, -90, 802, 24, 2, 66],排序后应得到 [-90, -45, 2, 24, 66, 75, 170, 802]。
解题过程循序渐进讲解
步骤1:回顾标准基数排序(非负数版本)
- 基数排序假设每个整数由若干“位”组成(可以是十进制位,也可以是二进制位或其他进制)。
- 常用LSD(Least Significant Digit)方式:从最低位开始,对每一位进行一次稳定的排序(通常用计数排序作为子程序)。
- 例如对非负数组
[170, 45, 75, 90, 802, 24, 2, 66]排序:- 先按个位排序(0、5、5、0、2、4、2、6)→
[170, 90, 802, 2, 24, 45, 75, 66] - 再按十位排序(忽略个位)→
[802, 2, 24, 45, 66, 170, 75, 90] - 最后按百位排序 →
[2, 24, 45, 66, 75, 90, 170, 802]
- 先按个位排序(0、5、5、0、2、4、2、6)→
步骤2:问题分析——负数带来的挑战
- 整数在计算机中以补码表示,最高位是符号位(1表示负数,0表示非负数)。
- 如果直接按位排序(尤其是二进制位),负数的高位是1,会排在正数(高位为0)后面,导致错误顺序。
- 例如用LSD按二进制位排序,负数会排在正数之后,而不是按实际数值大小排序。
- 我们需要将负数转换为可比较的无符号形式,或者分别处理负数和非负数。
步骤3:解决方案1——偏移量法(最常用)
- 思路:将所有数加上一个偏移量(offset),使最小负数变为非负数,排序后再减去偏移量。
- 具体步骤:
a. 遍历数组,找到最小值minVal(可能是负数)。
b. 设偏移量offset = -minVal,使minVal + offset = 0。
c. 将所有元素加上offset,得到非负数组。
d. 对这个非负数组执行标准基数排序。
e. 排序后,将所有元素减去offset,恢复原值。 - 示例:数组
[170, -45, 75, -90, 802, 24, 2, 66]minVal = -90,offset = 90- 加偏移后:
[260, 45, 165, 0, 892, 114, 92, 156] - 排序后:
[0, 45, 92, 114, 156, 165, 260, 892] - 减偏移后:
[-90, -45, 2, 24, 66, 75, 170, 802]✅
步骤4:解决方案2——分离处理法
- 思路:将数组分为负数和非负数两部分,分别排序后合并。
- 具体步骤:
a. 分离负数和非负数。
b. 对负数部分取绝对值,然后进行标准基数排序,得到绝对值从小到大的序列。
c. 由于负数绝对值越大实际值越小(如-90 < -45),所以对排序后的负数部分反转顺序,再恢复负号。
d. 对非负数部分直接进行标准基数排序。
e. 合并两部分(负数在前,非负数在后)。 - 示例:同上数组
- 负数:
[-45, -90]→ 绝对值[45, 90]→ 排序后[45, 90]→ 反转[90, 45]→ 恢复负号[-90, -45] - 非负数:
[170, 75, 802, 24, 2, 66]→ 排序后[2, 24, 66, 75, 170, 802] - 合并:
[-90, -45, 2, 24, 66, 75, 170, 802]✅
- 负数:
步骤5:实现细节(以偏移量法为例,LSD基数排序,十进制位)
- 计数排序子函数(针对某一位):
- 输入:数组
arr,当前位exp(1, 10, 100, ...) - 统计每个数字(0-9)的出现次数。
- 计算前缀和,得到每个数字的结束位置。
- 从后向前遍历原数组,放入输出数组的对应位置(保证稳定性)。
- 输入:数组
- 主函数:
- 找最小值,加偏移量。
- 找最大值,确定最大位数。
- 对每一位调用计数排序。
- 减偏移量,恢复原值。
步骤6:复杂度分析
- 时间复杂度:O(d * (n + k)),其中 n 是元素个数,d 是最大位数,k 是进制数(十进制则 k=10)。
- 空间复杂度:O(n + k)(计数排序需要输出数组和计数数组)。
- 扩展负数处理不改变渐近复杂度,仅增加 O(n) 的偏移量操作。
步骤7:边界情况与注意事项
- 全部是负数或全部是非负数时,算法仍正确。
- 偏移量可能导致整数溢出(如果最小值是
Integer.MIN_VALUE,加偏移会溢出)。解决方案:- 使用更大类型(如
long)存储偏移后的值。 - 或采用分离处理法避免溢出。
- 使用更大类型(如
- 如果数值范围极大但元素很少,基数排序可能不如比较排序高效,需根据数据特征选择算法。