【演算法筆記】計數排序 / 桶排序 / 基數排序
【演算法筆記】計數排序 / 桶排序 / 基數排序
大家好我是 LukeTseng!這篇是我的一個小小學習筆記,為了想要了解更快速的排序法,所以特別製作了此篇。
在總結部分會將這三種排序法的各個優缺點,以及其他屬性等等做比較。
總之廢話不多說,進入正題吧:
計數排序法(Counting Sort)
計數排序法(Counting Sort)是一個有線性時間成長的排序法,相較於以往較為基礎的泡沫排序法(Bubble Sort)、插入排序法(Insertion Sort)(時間複雜度:O(n^2))等,或是適用於大資料且排序速度較快的排序法:快速排序法(Quick Sort)、合併排序法(Merge Sort)(時間複雜度:O(n log n)),都還要來得快。
它的時間複雜度是 O(n),如果要準確一點說的話,是 O(n+k),k 是資料量。
適用情況在於資料量少的時候,有多少呢?資料量在幾千個以內,且資料範圍不超過幾百個(例如 1 到 100 或 1 到 500)的時候,計數排序法就仍有效率排序。
計數排序的演算步驟如下:
- 找出要排序的數列當中之最大值
- 初始化一個長度為 max+1 的列表如
counting_list(因為我對 Python 最熟,故先用列表稱呼),所有元素都為 0。 - 將原本數列中的元素做「計數」,而將計數結果寫入
counting_list對應的索引值。- 比如說原數列 =
[1,2,3,4,5,2,3,2,1,0,0],那就把裡面每個數字出現的次數算出來,稱為計數。然後因為最大值是 5,所以 counting_list 總共會有 6 項元素,索引值就等於原數列的數字(0~5)。最終counting_list = [2,2,2,2,1,1],裡面數字對應代表 0 ~ 5 出現的次數。
- 比如說原數列 =
- 進行
counting_list[i] = counting_list[i – 1] + counting_list[i](項數跟前一項元素相加),讓每個元素存儲的是待排序數列中小於或等於該元素的數字個數。 - 將原始數列從末端開始迭代,根據
counting_list將每個元素放置到新的排序數列中,然後將對應的counting_list值減 1。
有關 5.,原始數列從末端開始迭代,
假設原數列 = [1,2,3,4,5,2,3,2,1,0,0],則會從 0 開始,那這個 0 會去找 counting_list 索引為 0 的地方,然後將索引 0 的元素減 1,得到索引 1,再把 0 放到新的已排序數列的索引 1(從 counting_list 的元素 - 1 得來)。
接下來就以此類推,原始數列的下一個元素 0,同樣找到 counting_list 索引為 0 的元素,再扣 1,得到索引 0,放進去新的已排序數列的索引 0。
以下是計數排序的動圖以及說明(Source:typescript-algorithms/src/algorithms/sorting/counting-sort/README.md at master · Lugriz/typescript-algorithms):

在 Count Array C. 對原始數列做前綴和這件事情:


Python 實作
註:前綴和(prefix sum)就是前面講的 counting_list[i] = counting_list[i – 1] + counting_list[i],目前項數與前項的和。
1 | def count_sort(input_array): |
Source:Counting Sort - Data Structures and Algorithms Tutorials - GeeksforGeeks
桶排序法(Bucket Sort)
看文字不懂?去看這支影片吧:How to Bucket Sort - Youtube
桶排序(Bucket Sort)很特別,跟以往的排序法用的演算方式不同,以往都是用比較排序,而桶排序則像是分配性的排序(非比較排序就對了)。
這個排序法適用於數值分佈範圍較小且資料相對均勻的情況。主要原理是將輸入資料分配到不同的「桶」(buckets)中,然後對每個桶內的資料進行單獨排序,最後再將所有桶內的資料合併,就能得到排序後的結果。
桶排序是一種拿空間換時間的排序法,桶排序的最佳和平均時間複雜度通常為 O(n + k),但最壞情況可能會到 O(n^2)。
桶排序的演算步驟如下:
- 建桶:建立定量的桶子。桶子的數量取決於待排序的數列元素範圍和精度。通常桶子的數量等於待排序的數列元素個數(數列元素個數 -> 意即一個數列的長度)。
- 分配:根據 範圍(比如說 0 ~ 10、11 ~ 19 …) 將待排序數列中的元素插入到桶中。
- 從待排序數列中取出每個元素。
- 將元素乘以桶數列的長度。
- 將結果(元素乘以桶數列的長度)轉換為整數(若有小數點就把小數點去掉,只留整數),可做為桶數列的索引。
- 將元素放入桶中,索引為之前計算出來的。
- 不斷重複上述步驟
- 桶內排序:將每個非空桶內的元素進行排序。
- 排序法用穩定的排序法下去排序(如:Bubble Sort、Merge Sort 等)
- 合併:收集每個非空桶中的元素並將其放回原始數列當中。
- 按順序迭代每個桶。
- 將桶中的每個單獨元素插入原始數列中。
- 一旦一個元素被複製,就會從桶中刪除。
- 對所有桶重複此過程,直到收集完所有元素。
Python 實作
1 | def insertion_sort(bucket): |
Source:Bucket Sort - Data Structures and Algorithms Tutorials - GeeksforGeeks
基數排序法(Radix Sort)
看文字不懂?去看這支影片吧:Radix Sort | GeeksforGeeks - Youtube
基數排序法(Radix Sort)是一種線性排序演算法,有基於「按位數排序」的概念。
這個排序法會把每個元素看作是由多個「位數」組成,並從最低有效位數(Least Significant Digit, LSD)到最高有效位數(Most Significant Digit, MSD)逐位進行排序。經過多次穩定的排序,得出完整的已排序數列。
基數排序的時間複雜度為 O(d*(n+k)),其中 d 是位數的數量,n 是數據的數量,k 是每位數的取值範圍(如十進制數的 k = 10)。
基數排序的演算步驟如下:
假設待排序數列 a = [170, 45, 75, 90, 802, 24, 2, 66]
- 找到數列當中最大的元素,即 802。因為它有 3 位數,所以進行 3 次排序。
- 第一輪(第一次排序)按個位數排序:
- 170 (0), 90 (0), 802 (2), 2 (2), 24 (4), 45 (5), 75 (5), 66 (6)
- 排序後:170, 90, 802, 2, 24, 45, 75, 66
- 註:(0)、(2) 是這些數字的個位數。
- 第二輪(第二次排序)按十位數排序:
- 802 (0), 2 (0), 24 (2), 45 (4), 66 (6), 170 (7), 75 (7), 90 (9)
- 排序後:802, 2, 24, 45, 66, 170, 75, 90
- 第三輪(第三次排序)按百位數排序:
- 2 (002), 24 (024), 45 (045), 66 (066), 75 (075), 90 (090), 170 (170), 802 (802)
- 排序後:2, 24, 45, 66, 75, 90, 170, 802(排序完成)
Python 實作
1 | def countingSort(arr, exp1): |
Source:Radix Sort - Data Structures and Algorithms Tutorials - GeeksforGeeks
總結
| 排序法 | 計數排序法(Counting Sort) | 桶排序法(Bucket Sort) | 基數排序法(Radix Sort) |
|---|---|---|---|
| 基本原理 | 根據數字出現的頻率來排序 | 將數據分散到多個桶中,桶內排序後合併 | 基於數位位置的多次穩定排序 |
| 時間複雜度 | O(n + k) | 最佳 / 平均:O(n + k);最壞:O(n^2) | O(d*(n + k)) |
| 空間複雜度 | O(k) | O(n + k) | O(n + k) |
| 穩定性 | Y | Y | Y |
| 原地演算法 | N | N | N |
| 適用情況 | 資料範圍小且為整數值 | 資料均勻分布 | 資料為整數或固定長度的字符串、資料範圍大但位數少 |
| 優點 | 線性時間複雜度、簡單易做、穩定 | 對均勻分布的資料效率高 | 對小數字有效、穩定 |
| 缺點 | 不適用於浮點數或大範圍資料、需要額外空間儲存計數數列 | 空間複雜度高、效率依賴於資料的分布 | 需要知道資料的範圍和位數、對於大範圍的資料不如計數排序快 |
原地演算法(或稱就地演算法:in-place algorithm):簡單來說就是空間複雜度很低的演算法,通常是 O(1),因為它只在原始資料結構上進行修改,不需要額外的空間來儲存中間結果。
參考資料
Counting Sort - Data Structures and Algorithms Tutorials - GeeksforGeeks
[演算法]計數排序(Counting sort). 原理 | by 安安我里夫啦 | Medium
演算法學習筆記:計數排序(Counting Sort)、基數排序(Radix Sort) - 拉爾夫的技術隨筆 - Medium
Bucket Sort - Data Structures and Algorithms Tutorials - GeeksforGeeks
桶排序 Bucket sort - Rust Algorithm Club
[演算法]桶排序(Bucket sort) - 安安我里夫啦 - Medium
Radix Sort - Data Structures and Algorithms Tutorials - GeeksforGeeks
基數排序 Radix sort - Rust Algorithm Club
[演算法] 學習筆記 — 13. 基數排序法 Radix Sort | by Amber Fragments | Medium



