堆積排序(Heap Sort)演算法是利用完全二元樹(Complete Binary Tree),也就是堆積(Heap)結構來完成排序的演算法。雖然說要用到堆積結構,看起來好像很複雜似的,但其實這個只要一般的陣列結構(可以直接用要排序的陣列來製作)就能實作出來,而且實作出來的速度保證不會太慢,再怎麼差都會有O(nlogn)的時間複雜度。
堆積排序法(Heap Sort)
堆積排序法的概念
堆積排序有法兩個大步驟,第一個是把要排序的陣列製作成「最小堆積」(Min Heap)或是「最大堆積」(Max Heap)。如果要將陣列遞增排序的話就使用最大堆積;如果要遞減排序的話就使用最小堆積。接下來的步驟就是利用最大堆積和最小堆積來進行排序,方法和建立最大堆積或是最小堆積時是幾乎一樣的。
什麼是完全二元樹?完全二元樹是一種二元樹,它只允許最後一層(最底下那層)的節點數量可以不必填滿(若頂層是第1層,則第n層的最大節點數量為2n-1)。例如以下結構即為完全二元樹:
5 / \ 1 3 / \ / 6 2 4
什麼是最小堆積?最小堆積就是上層節點數值必定會小於下層節點數值的完全二元樹。例如以下結構即為最小堆積:
1 / \ 2 5 / \ / 4 6 3
什麼是最大堆積?最大堆積就是上層節點數值必定會大於下層節點數值的完全二元樹。例如以下結構即為最大堆積:
6 / \ 5 3 / \ / 4 2 1
陣列可以依照索引順序,由左至右、由上到下表示出一個完全二元樹。例如以上的最大堆積,可以是以下這個陣列:
索引 0 1 2 3 4 5 數值 6 5 3 4 2 1
堆積排序法的過程
假設現在有個陣列資料,內容如下:
索引 0 1 2 3 4 5 6 7 8 9 數值 69 81 30 38 9 2 47 61 32 79
要如何將它遞增排列呢?
我們先將這個陣列用完全二元樹的結構來表示,如下:
69
/ \
/ \
/ \
81 30
/ \ / \
/ \ / \
38 9 2 47
/ \ /
61 32 79
由於我們要進行遞增,因此要先將這個完全二元樹變成最大堆積。於是我們從最下面的兩層開始,把比上層大的元素交換到上層。
首先看到47,由於它沒有子節點,因此還不用交換。
69
/ \
/ \
/ \
81 30
/ \ / \
/ \ / \
38 9 2 47*
/ \ /
61 32 79
2,也沒有子節點,因此不用交換。
69
/ \
/ \
/ \
81 30
/ \ / \
/ \ / \
38 9 2* 47
/ \ /
61 32 79
再來看到9,9有一個值為79的子節點。由於79比9大,所以要交換上來。
69
/ \
/ \
/ \
81 30
/ \ / \
/ \ / \
38 9* 2 47
/ \ /
61 32 79↑
69
/ \
/ \
/ \
81 30
/ \ / \
/ \ / \
38 79+ 2 47
/ \ /
61 32 9*
此時9已沒有子節點了,結束交換。
再來看到38,38有兩個子節點,最大的子節點為61。由於61比38大,所以要交換上來。
69
/ \
/ \
/ \
81 30
/ \ / \
/ \ / \
38* 79 2 47
/ \ /
61↑32 9
69
/ \
/ \
/ \
81 30
/ \ / \
/ \ / \
61+ 79 2 47
/ \ /
38* 32 9
此時38已沒有子節點了,結束交換。
依此類推,剩下的最大堆積的建構過程如下:
69
/ \
/ \
/ \
81 30*
/ \ / \
/ \ / \
61 79 2 47↑
/ \ /
38 32 9
69
/ \
/ \
/ \
81 47+
/ \ / \
/ \ / \
61 79 2 30*
/ \ /
38 32 9
69
/ \
/ \
/ \
81* 47
/ \ / \
/ \ / \
61 79 2 30
/ \ /
38 32 9
69*
/ \
/ \
/ \
81↑ 47
/ \ / \
/ \ / \
61 79 2 30
/ \ /
38 32 9
81+
/ \
/ \
/ \
69* 47
/ \ / \
/ \ / \
61 79↑ 2 30
/ \ /
38 32 9
81+
/ \
/ \
/ \
79 47
/ \ / \
/ \ / \
61 69* 2 30
/ \ /
38 32 9
81
/ \
/ \
/ \
79 47
/ \ / \
/ \ / \
61 69 2 30
/ \ /
38 32 9
至此,最大堆積就建立好了!
接下來的工作就是排序啦!由於我們現在已經知道這個結構中的最大值是最頂層的節點,且我們要把陣列做遞增排列。所以我們可以直接把頂層的節點,也就是最大值與陣列的最後一個元素做交換,如此一來我們的遞增排序就已經排好一個元素了。
9*
/ \
/ \
/ \
79↑ 47
/ \ / \
/ \ / \
61 69 2 30
/ \ /
38 32 81-
但別忘了要繼續維持住我們的最大堆積條件,所以還是得做交換才行。由於此時81已經在陣列的正確位置上,我們可以直接把它從最大堆積的考慮範圍中排除,不要再去動它了。
79+
/ \
/ \
/ \
9* 47
/ \ / \
/ \ / \
61 69↑ 2 30
/ \ /
38 32 81-
79+
/ \
/ \
/ \
69 47
/ \ / \
/ \ / \
61 9* 2 30
/ \ /
38 32 81-
79
/ \
/ \
/ \
69 47
/ \ / \
/ \ / \
61 9 2 30
/ \ /
38 32 81-
接下來就是讓32要與這個最大堆積結構中的最大值,也就是最頂層的節點做交換啦!之後的步驟都跟剛才把81換下來時一樣,如下:
32*
/ \
/ \
/ \
69↑ 47
/ \ / \
/ \ / \
61 9 2 30
/ \ /
38 79- 81-
69+
/ \
/ \
/ \
32* 47
/ \ / \
/ \ / \
61↑ 9 2 30
/ \ /
38 79- 81-
69+
/ \
/ \
/ \
61 47
/ \ / \
/ \ / \
32* 9 2 30
/ \ /
38↑79- 81-
69+
/ \
/ \
/ \
61 47
/ \ / \
/ \ / \
38 9 2 30
/ \ /
32* 79- 81-
69
/ \
/ \
/ \
61 47
/ \ / \
/ \ / \
38 9 2 30
/ \ /
32 79- 81-
32*
/ \
/ \
/ \
61↑ 47
/ \ / \
/ \ / \
38 9 2 30
/ \ /
69- 79- 81-
61+
/ \
/ \
/ \
32* 47
/ \ / \
/ \ / \
38↑ 9 2 30
/ \ /
69- 79- 81-
61+
/ \
/ \
/ \
38 47
/ \ / \
/ \ / \
32* 9 2 30
/ \ /
69- 79- 81-
61
/ \
/ \
/ \
38 47
/ \ / \
/ \ / \
32 9 2 30
/ \ /
69- 79- 81-
30*
/ \
/ \
/ \
38 47↑
/ \ / \
/ \ / \
32 9 2 61-
/ \ /
69- 79- 81-
47+
/ \
/ \
/ \
38 30*
/ \ / \
/ \ / \
32 9 2 61-
/ \ /
69- 79- 81-
47
/ \
/ \
/ \
38 30
/ \ / \
/ \ / \
32 9 2 61-
/ \ /
69- 79- 81-
2*
/ \
/ \
/ \
38↑ 30
/ \ / \
/ \ / \
32 9 47- 61-
/ \ /
69- 79- 81-
38+
/ \
/ \
/ \
2* 30
/ \ / \
/ \ / \
32↑ 9 47- 61-
/ \ /
69- 79- 81-
38+
/ \
/ \
/ \
32 30
/ \ / \
/ \ / \
2* 9 47- 61-
/ \ /
69- 79- 81-
38
/ \
/ \
/ \
32 30
/ \ / \
/ \ / \
2 9 47- 61-
/ \ /
69- 79- 81-
9*
/ \
/ \
/ \
32↑ 30
/ \ / \
/ \ / \
2 38- 47- 61-
/ \ /
69- 79- 81-
32+
/ \
/ \
/ \
9* 30
/ \ / \
/ \ / \
2 38- 47- 61-
/ \ /
69- 79- 81-
32
/ \
/ \
/ \
9 30
/ \ / \
/ \ / \
2 38- 47- 61-
/ \ /
69- 79- 81-
2*
/ \
/ \
/ \
9 30↑
/ \ / \
/ \ / \
32- 38- 47- 61-
/ \ /
69- 79- 81-
30+
/ \
/ \
/ \
9 2*
/ \ / \
/ \ / \
32- 38- 47- 61-
/ \ /
69- 79- 81-
30
/ \
/ \
/ \
9 2
/ \ / \
/ \ / \
32- 38- 47- 61-
/ \ /
69- 79- 81-
2*
/ \
/ \
/ \
9↑ 30-
/ \ / \
/ \ / \
32- 38- 47- 61-
/ \ /
69- 79- 81-
9+
/ \
/ \
/ \
2* 30-
/ \ / \
/ \ / \
32- 38- 47- 61-
/ \ /
69- 79- 81-
9
/ \
/ \
/ \
2 30-
/ \ / \
/ \ / \
32- 38- 47- 61-
/ \ /
69- 79- 81-
2
/ \
/ \
/ \
9- 30-
/ \ / \
/ \ / \
32- 38- 47- 61-
/ \ /
69- 79- 81-
這樣就完成排序啦!
堆積排序法的程式實作
>> 1等同於「除以2取整數」;<< 1等同於「乘以2」。
堆積排序法的複雜度
以#{{n}}#來表示要排序的資料筆數。
| 項目 | 值 | 備註 |
|---|---|---|
| 最差時間複雜度 | #{{O(n \log n)}}# | |
| 最佳時間複雜度 | #{{O(n)}}# | 要排序的元素值都一樣時。 |
| 平均時間複雜度 | #{{O(n \log n)}}# | |
| 最差空間複雜度 | #{{O(1)}}# | |
| 是否穩定 | 否 |


