基数排序全面详解:原理、Python实现与动图演示
1. 基数排序基本概念
基数排序(Radix Sort)是一种非比较型的整数排序算法,其核心思想是按照键值的每位数字来分配和收集元素。与基于比较的排序算法(如快速排序、归并排序)不同,基数排序通过多轮的"分配"和"收集"操作来实现排序,特别适合处理整数或固定长度字符串的排序问题。
1.1 算法特点
| 特性 | 说明 |
|---|---|
| 时间复杂度 | O(d×(n+k)),其中d是最大数字的位数,k是基数 |
| 空间复杂度 | O(n+k) |
| 稳定性 | 稳定排序算法 |
| 适用场景 | 整数、固定长度字符串排序 |
2. 基数排序工作原理
2.1 基本思想
基数排序采用"最低位优先"(LSD, Least Significant Digit first)或"最高位优先"(MSD, Most Significant Digit first)的策略。LSD从最低位开始排序,MSD从最高位开始排序。
算法步骤:
- 找到数组中最大数,确定最大位数
- 从最低位开始,按照每个位数进行排序
- 使用稳定的排序算法对每个位数进行排序(通常用计数排序)
- 重复步骤2-3,直到最高位排序完成
2.2 动图演示原理
虽然无法直接嵌入动图,但可以通过文字描述动图演示过程:
初始数组: [170, 45, 75, 90, 2, 802, 24, 66]
第1轮(个位排序):
- 按个位数分配到桶中:
桶0: 170, 90
桶2: 2, 802
桶4: 24
桶5: 45, 75
桶6: 66
- 收集: [170, 90, 2, 802, 24, 45, 75, 66]
第2轮(十位排序):
- 按十位数分配到桶中:
桶0: 2, 802
桶2: 24
桶4: 45
桶6: 66
桶7: 170, 75
桶9: 90
- 收集: [2, 802, 24, 45, 66, 170, 75, 90]
第3轮(百位排序):
- 按百位数分配到桶中:
桶0: 2, 24, 45, 66, 75, 90
桶1: 170
桶8: 802
- 收集: [2, 24, 45, 66, 75, 90, 170, 802]
最终得到有序数组。
3. Python实现基数排序
3.1 基础版本实现
python
def radix_sort(arr):
"""
基数排序算法实现(LSD方式)
参数:
arr: 待排序的整数列表
返回:
排序后的整数列表
"""
# 找到最大数,确定最大位数
if not arr:
return arr
max_num = max(arr)
exp = 1 # 当前处理的位数(1表示个位,10表示十位,以此类推)
# 当max_num // exp > 0时,继续排序
while max_num // exp > 0:
# 使用计数排序对当前位数进行排序
counting_sort_for_radix(arr, exp)
exp *= 10
return arr
def counting_sort_for_radix(arr, exp):
"""
用于基数排序的计数排序子函数
参数:
arr: 待排序数组
exp: 当前处理的位数
"""
n = len(arr)
output = [0] * n # 输出数组
count = [0] * 10 # 计数数组(0-9)
# 统计每个数字出现的次数
for i in range(n):
index = (arr[i] // exp) % 10
count[index] += 1
# 计算累积计数
for i in range(1, 10):
count[i] += count[i - 1]
# 构建输出数组(从后往前保持稳定性)
i = n - 1
while i >= 0:
index = (arr[i] // exp) % 10
output[count[index] - 1] = arr[i]
count[index] -= 1
i -= 1
# 将排序结果复制回原数组
for i in range(n):
arr[i] = output[i]
# 测试示例
if __name__ == "__main__":
test_arr = [170, 45, 75, 90, 2, 802, 24, 66]
print("原始数组:", test_arr)
sorted_arr = radix_sort(test_arr)
print("排序后数组:", sorted_arr)
3.2 完整功能版本
python
def advanced_radix_sort(arr, radix=10):
"""
增强版基数排序,支持自定义基数
参数:
arr: 待排序数组
radix: 基数(默认10进制)
返回:
排序后的数组
"""
if len(arr) <= 1:
return arr
# 计算最大数的位数
max_val = max(arr)
max_digit = 0
while max_val > 0:
max_digit += 1
max_val //= radix
# 逐位排序
for digit in range(max_digit):
# 创建桶
buckets = [[] for _ in range(radix)]
# 分配元素到桶中
for num in arr:
# 获取当前位的数字
current_digit = (num // (radix ** digit)) % radix
buckets[current_digit].append(num)
# 收集桶中的元素
arr = []
for bucket in buckets:
arr.extend(bucket)
return arr
# 测试不同场景
def test_radix_sort():
"""测试基数排序的各种场景"""
# 测试1:普通整数排序
test1 = [329, 457, 657, 839, 436, 720, 355]
print("测试1 - 普通整数:")
print("排序前:", test1)
print("排序后:", advanced_radix_sort(test1))
# 测试2:包含负数的排序(需要特殊处理)
test2 = [123, -45, 67, -89, 0, 456, -123]
print("
测试2 - 包含负数:")
print("排序前:", test2)
# 处理负数的方法:分离正负数分别排序
positive = [x for x in test2 if x >= 0]
negative = [-x for x in test2 if x < 0]
positive_sorted = advanced_radix_sort(positive)
negative_sorted = advanced_radix_sort(negative)
final_sorted = [-x for x in reversed(negative_sorted)] + positive_sorted
print("排序后:", final_sorted)
# 测试3:大量数据排序
import random
test3 = [random.randint(0, 10000) for _ in range(20)]
print("
测试3 - 大量数据:")
print("排序前:", test3[:10], "...") # 只显示前10个
sorted_test3 = advanced_radix_sort(test3)
print("排序后:", sorted_test3[:10], "...")
if __name__ == "__main__":
test_radix_sort()
4. 算法复杂度分析
4.1 时间复杂度
基数排序的时间复杂度为 O(d×(n+k)),其中:
- d:最大数字的位数
- n:数组元素个数
- k:基数(通常为10)
当d为常数且k=O(n)时,时间复杂度可视为O(n)。
4.2 空间复杂度
空间复杂度为 O(n+k),主要用于:
- 输出数组:O(n)
- 计数数组:O(k)
4.3 性能对比
| 排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
|---|---|---|---|---|---|
| 基数排序 | O(d×(n+k)) | O(d×(n+k)) | O(d×(n+k)) | O(n+k) | 稳定 |
| 快速排序 | O(n log n) | O(n log n) | O(n²) | O(log n) | 不稳定 |
| 归并排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | 稳定 |
| 堆排序 | O(n log n) | O(n log n) | O(n log n) | O(1) | 不稳定 |
5. 应用场景与优缺点
5.1 适用场景
- 整数排序:特别适合处理大量整数的排序问题
- 固定长度字符串排序:如电话号码、身份证号等
- 多关键字排序:当需要按多个条件排序时
- 数据范围较小但数量大的情况
5.2 优点
- 线性时间复杂度:在特定条件下可以达到线性时间复杂度
- 稳定性:保持相等元素的相对顺序
- 可预测性:时间复杂度稳定,不受输入数据分布影响
5.3 缺点
- 适用范围有限:主要适用于整数和固定长度字符串
- 空间开销:需要额外的存储空间
- 基数依赖:性能受选择的基数影响
6. 实际应用案例
python
class StudentSorter:
"""
使用基数排序对学生信息进行多关键字排序
"""
@staticmethod
def sort_students_by_score(students):
"""
按分数排序学生(假设分数为0-1000的整数)
"""
scores = [student['score'] for student in students]
sorted_scores = radix_sort(scores)
# 构建分数到学生的映射
score_to_students = {}
for student in students:
score = student['score']
if score not in score_to_students:
score_to_students[score] = []
score_to_students[score].append(student)
# 按排序后的分数顺序重组学生列表
sorted_students = []
for score in sorted_scores:
if score in score_to_students and score_to_students[score]:
sorted_students.append(score_to_students[score].pop())
return sorted_students
@staticmethod
def sort_by_multiple_criteria(students):
"""
多关键字排序:先按班级,再按学号
"""
# 提取班级和学号信息
class_numbers = [student['class'] for student in students]
student_ids = [student['id'] for student in students]
# 先按学号排序(次要关键字)
temp_sorted = advanced_radix_sort(list(range(len(students))),
key=lambda x: student_ids[x])
# 再按班级排序(主要关键字)
final_sorted = advanced_radix_sort(temp_sorted,
key=lambda x: class_numbers[x])
return [students[i] for i in final_sorted]
# 使用示例
students = [
{'name': '张三', 'score': 856, 'class': 3, 'id': 1003},
{'name': '李四', 'score': 723, 'class': 1, 'id': 1001},
{'name': '王五', 'score': 856, 'class': 2, 'id': 1002},
]
print("按分数排序:")
sorted_by_score = StudentSorter.sort_students_by_score(students)
for student in sorted_by_score:
print(f"{student['name']}: {student['score']}分")
基数排序作为一种高效的线性时间排序算法,在合适的应用场景下能够提供优异的性能表现。通过理解其原理和实现方式,可以在实际编程中更好地选择和应用这种算法。