我用一个生动形象的比喻来帮你理解**计数排序(Counting Sort)**的原理。
计数排序的生动形象解释
想象你是一个老师,要给一群学生按考试成绩从低到高排队。成绩都是整数,范围是0到100分。
传统排序的难点
如果你让学生们两两比较成绩,排队可能很慢,尤其学生很多时。
计数排序的妙招
你决定先准备一个“成绩统计表”,表里有101个格子(0分到100分),每个格子用来记录有多少学生得了这个分数。
具体步骤
-
统计成绩
你让每个学生告诉你自己的成绩,然后你在对应分数的格子里加1。
比如有3个学生得了85分,85分格子里的数字就是3。 -
计算累计人数
你从0分开始,依次把每个分数格子的数量加起来,得到“累计人数”。
这样你就知道,所有成绩小于等于某分数的学生一共有多少。 -
确定每个学生的位置
你再从后往前看每个学生的成绩,利用累计人数表,确定这个学生在排队中的准确位置。
比如累计人数告诉你,85分的学生排在第50到第52位。 -
排好队
按照计算的位置,把学生排好队,成绩从低到高。
为什么这样能排序?
- 你不需要比较学生之间的成绩,只用统计每个成绩出现的次数。
- 利用累计人数,直接算出每个成绩的学生应该排在哪个位置。
- 这样就能快速把学生排成有序队伍。
总结
- 计数排序就是“统计-累计-定位-排序”的过程。
- 它利用成绩的范围有限,快速统计出现次数。
- 适合整数且范围不大的数据。
小结表格
步骤 | 操作描述 | 作用 |
---|---|---|
统计 | 统计每个成绩出现的次数 | 了解数据分布 |
累计 | 计算累计人数 | 确定每个成绩的排队区间 |
定位 | 根据累计人数确定每个元素位置 | 直接定位元素排序位置 |
排序 | 按位置把元素放入结果数组 | 得到有序序列 |
我们继续深入讲解计数排序的细节,包括它的实现步骤、适用场景、时间复杂度分析,以及一个简单的代码示例,帮助你更全面地理解计数排序。
计数排序的详细步骤
-
确定数据范围
找出待排序数组中的最大值和最小值,确定计数数组的大小。 -
统计元素出现次数
创建一个计数数组,长度为最大值减最小值加1,遍历原数组,统计每个元素出现的次数,存入计数数组对应位置。 -
计算累计计数
对计数数组进行累加,得到每个元素在排序后数组中的结束位置。 -
构建排序结果
从原数组末尾开始遍历,根据计数数组确定每个元素在结果数组中的位置,放入结果数组,并将计数减1。
计数排序的适用场景
-
整数排序
计数排序适合整数排序,尤其是数据范围不大的情况。 -
数据范围有限
当数据范围(最大值-最小值)不是特别大时,计数排序效率高。 -
稳定排序需求
计数排序是稳定排序,适合需要保持相同元素相对顺序的场景。
计数排序的时间复杂度分析
-
时间复杂度:O(n + k),其中n是元素数量,k是数据范围大小。
- 统计次数:O(n)
- 计算累计:O(k)
- 构建结果:O(n)
-
空间复杂度:O(n + k),需要额外空间存储计数数组和结果数组。
计数排序的Python代码示例
def counting_sort(arr):
if not arr:
return arr
min_val = min(arr)
max_val = max(arr)
range_val = max_val - min_val + 1
# 1. 统计元素出现次数
count = [0] * range_val
for num in arr:
count[num - min_val] += 1
# 2. 计算累计计数
for i in range(1, range_val):
count[i] += count[i - 1]
# 3. 构建排序结果
output = [0] * len(arr)
for num in reversed(arr):
count[num - min_val] -= 1
output[count[num - min_val]] = num
return output
# 测试
arr = [4, 2, 2, 8, 3, 3, 1]
sorted_arr = counting_sort(arr)
print(sorted_arr)
# 输出: [1, 2, 2, 3, 3, 4, 8]
代码解析
- 先确定数据范围,创建计数数组。
- 统计每个元素出现次数。
- 计算累计计数,确定元素排序位置。
- 从后往前遍历原数组,保证排序稳定性。
- 返回排序后的数组。
总结
- 计数排序通过统计元素出现次数,利用累计计数直接定位元素位置。
- 适合整数且范围不大的数据,时间复杂度接近线性。
- 是稳定排序,空间开销较大。