算法分析--基数排序

算法分析–基数排序

1. 什么是基数排序?一个直观的比喻想象一下,你要整理一堆快递包裹,这些包裹上都有三位数的数字编码(比如 123、456、789)。你想让它们按数字从小到大排列。一个简单的办法是:先看个位数,把个位数相同的包裹放在一起;然后看十位数,再把十位数相同的调整顺序;最后看百位数。这样经过三次“分类”,所有包裹就自动排好序了。这就是基数排序的核心思想——它是一种非比较型整数排序算法,通过按位(个位、十位、百位…)分配和收集来实现排序。与快速排序、归并排序等基于比较的算法不同,基数排序不依赖元素之间的直接比较,而是利用数字的位数特性。## 2. 算法原理与步骤基数排序的原理非常直观,可以分为三个主要步骤:1. 确定最大位数:找到数组中最大数字的位数,这决定了我们需要进行多少轮排序。2. 逐位分配:从最低有效位(个位)开始,根据当前位的数字将元素分配到10个桶(0-9)中。3. 逐位收集:按桶的顺序(0-9)将元素重新收集到数组中,保持桶内的相对顺序。这个过程重复进行,直到处理完所有位数。注意:我们通常从最低位(LSD,Least Significant Digit)开始,也可以从最高位(MSD)开始,但LSD实现更简单。关键特性:- 时间复杂度:O(d * (n + k)),其中d是位数,n是元素个数,k是基数(通常为10)- 空间复杂度:O(n + k)- 稳定性:稳定排序(相同数字的相对顺序保持不变)## 3. 代码示例一:基础版基数排序(整数排序)让我们先看一个简单的Python实现,对非负整数进行升序排序:pythondef counting_sort_by_digit(arr, exp): """根据指定的位(exp=1,10,100...)对数组进行计数排序""" n = len(arr) output = [0] * n # 输出数组 count = [0] * 10 # 计数数组,0-9共10个数字 # 统计当前位上每个数字出现的次数 for i in range(n): index = (arr[i] // exp) % 10 # 提取当前位上的数字 count[index] += 1 # 将计数转换为累积计数(决定每个数字在输出数组中的位置) for i in range(1, 10): count[i] += count[i - 1] # 从后向前遍历,确保稳定性(相同数字不会乱序) for i in range(n - 1, -1, -1): index = (arr[i] // exp) % 10 output[count[index] - 1] = arr[i] count[index] -= 1 # 将排序后的结果复制回原数组 for i in range(n): arr[i] = output[i]def radix_sort_positive(arr): """对非负整数数组进行基数排序""" if len(arr) == 0: return arr # 找到最大数字,确定位数 max_num = max(arr) exp = 1 # 从个位开始(10^0) # 当最大数字还有更高位时继续循环 while max_num // exp > 0: counting_sort_by_digit(arr, exp) exp *= 10 # 前进到下一位 return arr# 测试代码test_arr = [170, 45, 75, 90, 802, 24, 2, 66]print("原始数组:", test_arr)sorted_arr = radix_sort_positive(test_arr)print("排序后数组:", sorted_arr)代码解析:- counting_sort_by_digit 是核心辅助函数,它基于当前位(个位、十位…)进行计数排序- 使用倒序遍历保证了排序的稳定性——相同数字的相对顺序不变- 外层循环 while max_num // exp > 0 控制位数,直到最大数字的最高位处理完毕运行这段代码,你会看到:原始数组: [170, 45, 75, 90, 802, 24, 2, 66]排序后数组: [2, 24, 45, 66, 75, 90, 170, 802]## 4. 代码示例二:支持负数的改进版实际应用中,数组经常包含负数。上面的基础版无法处理负数,因为负数的数字位提取会出问题。下面我们扩展一下,让基数排序支持负数:pythondef radix_sort_with_negative(arr): """ 支持负数的基数排序 思路:将负数单独分离,取绝对值排序后取反,再与正数合并 """ if len(arr) <= 1: return arr # 分离正负数 negatives = [-x for x in arr if x < 0] # 负数取绝对值 positives = [x for x in arr if x >= 0] # 对正数部分进行基数排序 if positives: max_positive = max(positives) exp = 1 while max_positive // exp > 0: counting_sort_by_digit(positives, exp) exp *= 10 # 对负数部分(绝对值)进行基数排序 if negatives: max_negative = max(negatives) exp = 1 while max_negative // exp > 0: counting_sort_by_digit(negatives, exp) exp *= 10 # 负数排序后要反转(因为绝对值越大,原数越小) # 例如:[-5, -3] -> 绝对值[5, 3]排序后->[3, 5] -> 取反->[-3, -5] -> 反转->[-5, -3] negatives_sorted = [-x for x in negatives][::-1] # 合并结果:负数在前,正数在后 return negatives_sorted + positives# 测试包含负数的数组test_arr2 = [3, -1, 0, -5, 8, -3, 2, -8]print("原始数组(含负数):", test_arr2)sorted_arr2 = radix_sort_with_negative(test_arr2)print("排序后数组:", sorted_arr2)代码解析:- 核心技巧:将负数转换为正数(取绝对值)处理,排序后再转换回来- 负数部分排序后需要反转:因为绝对值越大,原数越小(例如 -8 < -3)- 最终合并时负数在前(升序排列),正数在后运行结果:原始数组(含负数): [3, -1, 0, -5, 8, -3, 2, -8]排序后数组: [-8, -5, -3, -1, 0, 2, 3, 8]## 5. 性能分析与应用场景### 时间复杂度- 最好情况:O(d * (n + k))- 最坏情况:O(d * (n + k)) - 平均情况:O(d * (n + k))其中:- n = 元素个数- d = 最大位数(如3位数的d=3)- k = 基数(十进制为10)注意:当d较小(比如固定长度整数)时,基数排序的时间复杂度接近O(n),比O(n log n)的快速排序更快。### 空间复杂度- O(n + k),需要额外的计数数组和输出数组### 与比较排序的对比| 算法 | 时间复杂度 | 空间复杂度 | 稳定性 ||------|------------|------------|--------|| 快速排序 | O(n log n) | O(log n) | 不稳定 || 归并排序 | O(n log n) | O(n) | 稳定 || 基数排序 | O(d * (n+k)) | O(n+k) | 稳定 |### 适用场景1. 固定位数的整数排序:如电话号码、身份证号、邮政编码2. 数据范围较大但位数有限:比如10万个数,范围0-10^9,但只有10位数字3. 需要稳定排序:比如多关键字排序(先按年龄排,再按姓名排)### 不适用场景1. 浮点数:位数不固定,且基数处理复杂(可以通过乘以10的幂次转换为整数)2. 字符串:需要按字符编码处理,但长度不一3. 数据位数差异极大:比如1和1000000000,会导致大量的空桶操作## 6. 总结基数排序是一种优雅而高效的排序算法,它通过“按位分配”巧妙绕开了元素比较的瓶颈。从算法原理上看,它体现了分治思想:将一个复杂问题(多位数排序)分解为多个简单问题(单数字排序)。从实现角度看,它依赖计数排序作为子过程,利用了计数排序的稳定性和线性时间复杂度。虽然基数排序不适用于所有场景(比如浮点数或字符串),但在处理固定位数的整数时,它往往能展现出惊人的速度——特别是当数据量很大而位数较小的时候。结合代码示例,我们可以看到基数排序的实现并不复杂,而且通过一些技巧(如处理负数),它可以适应更广泛的应用场景。最后,记住基数排序的核心口诀:“从低位到高位,分桶收集稳又好”。理解了这个思想,你就能轻松掌握这种非比较型排序的精髓。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值