话说,一口气不能吃个胖子,
一次性 学习 计数排序, 也确实容易消化不良.
下面,我们逐步学习下计数排序.
1. 已知一个简单列表 l1 = [5, 4, 3],
分析下这个列表的情况
5 > 4, 5 > 3, 所以 5 比列表中其他数大 的次数, 是 2
4 > 3, 4 < 5, 所以 4 比列表中其他数大 的次数, 是 1
3 < 4, 3 < 5, 所以 4 比列表中其他数大 的次数, 是 0
注意到了吗?
比 其他数大的次数, 序列 是 2 -> 5
1 -> 4
0 -> 3
如此,计数排序的核心原理就呼之欲出了.
统计每个数比列表其他数 大于 的次数,
次数越多 说明, 这个数越大, 反之, 大于的次数越少, 说明, 这个数就越小
下面,上代码:
def sort(l):
n = len(l)
res = [None] * n # 结果列表
# 首次循环遍历, 每个列表的数都统计
for i in range(n):
# p 表示 a[i] 大于列表其他数 的次数
p = 0
# 二次循环遍历, 列表中的每个数都和首次循环的数比较
for j in range(n):
if l[i] > l[j]:
p += 1
res[p] = l[i]
return res print(sort([5, 4, 3])) 打印结果是: [3, 4, 5]
外层循环, 遍历列表每个数,
内层循环, 外层循环拿到的数 和 内层循环遍历的数, 逐个比较, 如果 外层循环 大于, 则 计数值 p + 1
内层循环结束, 则把外层循环的数, 放在 结果列表 的 索引 为 p 的位置上
2. 上面代码, 对于 列表中 , 如果数值, 都不相等, 则是ok的.
如果列表 是 [5, 5, 3] , 打印结果 是 [3, 5, None].
为什么结果就不正确了呢?
因为 有两个 5, 计算的结果都是 p = 1
所以, 两个 5 , 都被放在结果列表 索引 1 的位置上了,
索引 2 的位置,没有放数.
优化后的代码逻辑如下:
def sort(l):
n = len(l)
res = [None] * n
# 首次循环遍历, 每个列表的数都统计
for i in range(n):
# p 表示 a[i] 大于列表其他数 的次数
p = 0
# q 表示 等于 a[i] 的次数
q = 0
# 二次循环遍历, 列表中的每个数都和首次循环的数比较
for j in range(n):
if l[i] > l[j]:
p += 1
elif l[i] == l[j]:
q += 1
for k in range(p, p+q): # q表示 相等的次数,就表示, 从 P 开始索引后, 连续 q 次,都是同样的 数
res[k] = l[i]
return res print(sort([5, 5, 3])) 打印结果是: [3, 5, 5]
上述改进代码,容易困惑的地方: 是 16 - 17 行代码,
print(k, p, q, p+q) 建议读者, 在 16 和 17 之间, 加入 如上代码, 打印看看 16, 17行代码, 逻辑上也不完美,
比如, 两个 5, 就会被 重复赋值