【排序演算法】堆排序初探
【排序演算法】堆排序初探
定義
堆(heap)是一類特殊的資料結構的統稱。
堆通常是一個可以被看做一棵完全二叉樹的陣列物件。
About Binary Trees(二叉樹相關)
Binary Tree(二叉樹): 是樹的一種,主要的特點是二叉樹的所有節點最多隻有兩個葉節點。除此之外沒有別的要求。
Complete Binary Tree(完全二叉樹): 二叉樹的一種。在完全二叉樹當中,除了最後一層之外,所有層的節點都是滿的,且最後一層的節點也是從左到右的。優先填滿左邊的節點。
About Binary Heap(二叉堆相關)
Heap(堆): 堆是一種樹。在樹的性質之外,堆要求節點按照大小(父節點比子節點大/父節點比子節點小)來排列。
Binary Heap(二叉堆): 二叉堆是近似於完全二叉樹的一種堆。除完全二叉樹的性質外,二叉堆還要求堆內元素按照大小排列。
Min Heap(最小堆): 最小的鍵值總是在最前面。換句話說,所有的父節點都比他們的子節點小。
Max Heap(最大堆): 最大的鍵值總是在最前面。換句話說,所有的父節點都比他們的子節點大。
作者:匿名使用者
連結:https://www.zhihu.com/question/36134980/answer/1202061173
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
思想
堆排序(Heapsort)是指利用堆這種資料結構所設計的一種排序演算法。
堆的性質保證了其根元素為最大或最小值;
每一次建立大/小頂堆,在堆頂取出最大/小值,把當前剩餘的最後一個元素放到堆頂;
再次建立大/小頂堆,得到全部資料中第二大/小的值,取出,把當前剩餘的最後一個元素放到堆頂;
重複上述步驟,直到全部元素都被取出。
假設一個小根堆的左、右子樹都已是堆,並且根的元素名為root ,其左右子節點為left 和right ,這種情況下,有兩種可能:
(2)root>left 或者 root>right,此時 root 應與兩個子女中值較小的一個交換,結果得到一個堆,除非 root 仍然大於其新子女的一個或全部的兩個。這種情況下,我們只需簡單地繼續這種將 “拉下來”的過程,直至到達某一個層使它小於它的子女,或者它成了葉結點。
步驟
以升序為例,需要不斷建立大頂堆
- 初始化大頂堆 (從倒數第二行的節點開始,從下往上、從右往左,這樣遍歷的順序保證了一個小根堆的左、右子樹都已是堆)
假設一個小根堆的左、右子樹都已是堆,並且根的元素名為root ,其左右子節點為left 和right ,這種情況下,有兩種可能:
(1) root<=left 並且 root<=right ,此時堆已完成;
(2)root>left 或者 root>right,此時 root 應與兩個子女中值較小的一個交換,結果得到一個堆,除非 root 仍然大於其新子女的一個或全部的兩個。這種情況下,我們只需簡單地繼續這種將 “拉下來”的過程,直至到達某一個層使它小於它的子女,或者它成了葉結點。 - 將陣列第一個元素(即根節點)與陣列最後一個元素交換(這時最後一個元素成為了最大的)
- 重塑大頂堆
- 重複2.3.步驟,直至只剩下根節點
程式碼實現
- Down——把i逐層向下比較,放到合適的地方;結合一定的呼叫前提,實現了把以i為堆頂的子堆變為大頂堆的功能。
void Down(int array[], int i, int size)
{
int parent = i;
int child = 2 * i + 1;
while (child < size)
{
//比較兩個兒子的大小(如果有兩個兒子的前提下)
//換成更大的那個去和父親比較
if (child + 1 < size && array[child] < array[child + 1])
{
child++;
}
//如果父親不比兩個兒子都大
//那麼交換父親和兒子
//並且繼續往下檢視以剛剛的兒子為根的子堆
if (array[parent] < array[child])
{
swap(array, parent, child);
parent = child;
child = 2 * child + 1;
}
//如果父親比兩個兒子都大,則大頂堆已經完成
//因為我們遍歷的順序保證了一個子堆的左右子樹都已經是大頂堆
else break;
}
}
- BuildHeap——將原無序陣列初始化為一個大頂堆
void BuildHeap(int array[], int size)
{
for (int i = size / 2 - 1; i >= 0; i--)
Down(array, i, size);
}
比如,按照順序,先對倒數第二排的節點呼叫Down函式,結果是,倒數第二排結合最後一排成為了一個個大頂堆;
然後迴圈來到倒數第三排的節點,再呼叫Down(由上述知,已經滿足了每個子堆的左右子樹都已經是大頂堆的前提),之後倒數第三、二、一排共同組成了一個個大頂堆;
以此類推,最終構建出了一個真正的大頂堆。
- HeapSort——實現堆排序
void HeapSort(int array[], int size)
{
//初始化一個大頂堆
BuildHeap(array, size);
//將堆頂元素和堆中剩餘的最後一個元素交換
//再把交換後的堆頂放置到合適的地方
//由於只改變了堆頂的元素,不會改變其他子堆已經是大頂堆的事實
for (int i = size - 1; i > 0; i--)
{
swap(array, 0, i);
Down(array, 0,i);
}
}
- main
int main()
{
int array[] = { 20, 19, 18, 30, 13, 30, 27, 15, 10 };
int size = sizeof(array) / sizeof(int);
HeapSort(array, size);
for (int i = 0; i < size; i++)
cout << array[i] << ' ';
}
輸出結果