Menu
CoddyTech

基数排序

最后更新

基数排序是一种用于整数的非比较排序。它不比较数值的大小,而是逐位对数字排序。最低位优先(LSD)版本先处理个位,再处理十位,然后是百位,每一位都使用稳定的计数排序。因为每一轮都是稳定的,所以处理完最高位后,整个数组就有序了。点击上方的“播放”,观看每一轮按位排序如何重新排列这些条形。

基数排序的时间复杂度是 O(d·(n + k)),其中 d 是位数,k 是基数(这里是 10)。对于固定位宽的整数,这实际上是线性时间,可以胜过 O(n log n) 的比较排序,但它只适用于能拆分成数位或键的数据。

时间复杂度与空间复杂度

情况复杂度说明
时间O(d·(n + k))d 位数,基数 k(d 固定时为线性)
空间O(n + k)输出数组 + 各数位的计数
稳定性稳定每一轮都是稳定的计数排序
是否比较?否按数位排序,不比较数值大小
适用于整数/键不适用于一般的可比较对象

分步说明

步骤发生了什么
1找出最大值,确定需要处理多少位。
2从最低位(个位)开始。
3用计数排序按这一位对数组做稳定排序。
4移到下一个更高的数位。
5重复以上步骤,直到处理完所有数位。

实例演示

对 [170, 45, 75, 90, 2, 24, 66] 排序:

轮次数组操作
开始[170, 45, 75, 90, 2, 24, 66]最大值是 170,所以需要三轮按位排序。
个位[170, 90, 2, 24, 45, 75, 66]按个位稳定排序:0, 0, 2, 4, 5, 5, 6。
十位[2, 24, 45, 66, 170, 75, 90]按十位稳定排序:0, 2, 4, 6, 7, 7, 9(170 仍排在 75 前面)。
百位[2, 24, 45, 66, 75, 90, 170]按百位稳定排序;只有 170 的百位是 1,所以它移到最后。排序完成。

什么时候用基数排序

适合使用不适合使用
键是整数或定长字符串,可以拆分成数位。你必须用自定义比较器对任意对象排序。
键的位数 d 少且有上限,所以 O(d·(n + k)) 能胜过 O(n log n)。键非常长或没有上限,导致 d 很大,每一轮的开销都很高。
你需要稳定排序,并且能承受 O(n + k) 的额外空间。内存紧张,无法接受 O(n + k) 的缓冲区。
取值范围或基数 k 相对 n 不大。k 极大,每一轮计数排序都会成为运行时间的主要部分。

Radix Sort 代码

简洁、可运行的 Radix Sort 实现,提供 Python, JavaScript, Java, C++, C 版本。选择一种语言,复制代码,或在 Coddy 在线编辑器中直接打开。

Radix Sort 的 Python 代码

Python
1def radix_sort(a):2    # Sort by each decimal digit, least significant first3    max_value = max(a)4    exp = 15    while max_value // exp > 0:6        a = sort_by_digit(a, exp)7        exp *= 108    return a9
10
11def sort_by_digit(a, exp):12    buckets = [[] for _ in range(10)]13    for value in a:14        digit = (value // exp) % 1015        buckets[digit].append(value)16    # Concatenating buckets 0..9 keeps the sort stable17    return [value for bucket in buckets for value in bucket]18
19
20nums = [170, 45, 75, 90, 802, 24, 2, 66]21print("Before:", nums)22print("After: ", radix_sort(nums))
在 Python 在线编辑器中运行这段代码

基数排序常见问题

基数排序的时间复杂度是多少?
基数排序是 O(d·(n + k)),其中 d 是位数,k 是基数。对于固定位宽的整数,这实际上就是 O(n),可能比比较排序更快。它需要 O(n + k) 的额外空间。
基数排序是稳定的吗?

是的。LSD 基数排序在每一位上都依赖稳定的计数排序;正是这种稳定性,才让逐位排序的方法得到正确的有序结果。

什么时候可以用基数排序?

基数排序适用于能拆分成数位或固定大小键的数据,例如整数或定长字符串。它不是通用的比较排序,所以无法用自定义比较器对任意对象排序。

基数排序和计数排序有什么区别?
计数排序一轮就按单个键排好序,需要一个和取值范围一样大的计数数组,所以数值分布很广时效率会下降。基数排序逐位应用计数排序,每一轮的计数数组都很小(大小为基数 k),因此能处理普通计数排序应付不了的大取值范围。
LSD 基数排序为什么从最低位开始?

从最低位开始,每一轮稳定排序都能保留之前所有低位已经确定的顺序。等到处理最高位时,最高位相同的元素已经按低位排好了,所以整个数组最终完全有序。如果先从最高位排序,就会破坏这一点,需要改用另一种递归的方法(MSD 基数排序)。

基数排序能处理负数吗?

不能直接处理,基本的取数位操作假定数据是非负整数。常见的解决办法是给所有值加上一个偏移量(减去最小值),让它们都变成非负数;或者把负数和非负数分开排序,再拼接起来。把基数排序用在真实数据上时,忽略这一点是常见的 bug。

Coddy 编程语言插图

在 Coddy 掌握算法

开始