話說,一口氣不能吃個胖子,
一次性 學習 計數排序, 也確實容易消化不良.
下面,我們逐步學習下計數排序.
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
如此,計數排序的核心原理就呼之欲出了.
統計每個數比列表其他數 大於 的次數,
次數越多 說明, 這個數越大, 反之, 大於的次數越少, 說明, 這個數就越小
下面,上代碼:
1 def sort(l): 2 n = len(l) 3 res = [None] * n # 結果列表 4 # 首次循環遍歷, 每個列表的數都統計 5 for i in range(n): 6 # p 表示 a[i] 大於列表其他數 的次數 7 p = 0 8 # 二次循環遍歷, 列表中的每個數都和首次循環的數比較 9 for j in range(n): 10 if l[i] > l[j]: 11 p += 1 12 res[p] = l[i] 13 return res 14 15 16 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 的位置,沒有放數.
優化后的代碼邏輯如下:
1 def sort(l): 2 n = len(l) 3 res = [None] * n 4 # 首次循環遍歷, 每個列表的數都統計 5 for i in range(n): 6 # p 表示 a[i] 大於列表其他數 的次數 7 p = 0 8 # q 表示 等於 a[i] 的次數 9 q = 0 10 # 二次循環遍歷, 列表中的每個數都和首次循環的數比較 11 for j in range(n): 12 if l[i] > l[j]: 13 p += 1 14 elif l[i] == l[j]: 15 q += 1 16 for k in range(p, p+q): # q表示 相等的次數,就表示, 從 P 開始索引后, 連續 q 次,都是同樣的 數
17 res[k] = l[i] 18 return res 19 20 21 print(sort([5, 5, 3])) 打印結果是: [3, 5, 5]
上述改進代碼,容易困惑的地方: 是 16 - 17 行代碼,
print(k, p, q, p+q)
建議讀者, 在 16 和 17 之間, 加入 如上代碼, 打印看看
16, 17行代碼, 邏輯上也不完美,
比如, 兩個 5, 就會被 重復賦值