準備工作
1.堆的概念:
堆通常是一個可以被看做一棵樹的數組對象。在隊列中,調度程序反覆提取隊列中第一個作業並運行,因而實際情況中某些時間較短的任務將等待很長時間才能結束,或者某些不短小,但具有重要性的作業,同樣應當具有優先權。堆即爲解決此類問題設計的一種數據結構。(wiki上的定義)
在堆排序中,我們用到的是二叉堆(是一棵二叉樹的一種)。
2.最大堆(屬於二叉堆):
每個父結點的值都大於它的左右兩個子結點的值,在根節點存儲着堆裏所有數的最大值。
堆排序
1.堆修復:
0 4
此時我們要將其修復成一個最大堆,叫做堆修復。
設堆存在數組a[]中,下標從1開始,共有 n 個節點。
我們先比較一下根節點和其左右兒子的大小,找出最大的那個數的下標largest,有兩種情況:
(1)largest 不爲根節點(即是左右兒子中的一個)
此時,root (根節點)和 a[largest]交換,之後對largest繼續修復(採用遞歸)。
(2)largest是根節點,此時表明修復堆已經完成。
這樣是不是堆就修復完了呢? 當然不是。
注意到我們如果修復到了葉子節點,此時該修復工作也完成了,所以當左右某個兒子的下標大於了n時,本次修復結束(我們是採用遞歸來修復的,所以本次修復結束不意味着總修復結束)。
接下來我們就可以用代碼實現了:
void repair_heap(int a[], int pos, int sz)
{
int l = pos * 2;
int r = pos * 2 + 1;
int largest;
if(l <= sz && a[l] > a[pos])
largest = l;
else largest = pos;
if(r <= sz && a[r] > a[largest])
largest = r;
if(largest != pos)
{
swap(a[largest], a[pos]);
repair_heap(a, largest, sz);
}
}
不難看出,這一步的複雜度爲O(lgn).
sz(size)的作用在後面我們會看到。2.建立最大堆:
void build_maxheap(int a[], int pos)
{
int l = pos * 2;
int r = pos * 2 + 1;
if( l <= n )
build_maxheap( a, l );
if( r <= n)
build_maxheap( a, r );
repair_heap(a, pos, n);
}
3.排序:
怎麼實現將最大值存儲後再排出堆呢?
我們將第 n 個元素和 a[1] 交換,然後再讓堆的節點數減1(這個就通過sz來實現,我們儘量不改變n)。
接下來我們從a[ 1 ]開始修復堆就行了,以此循環,最終達到排序的效果。
代碼如下:
void heap_sort(int a[])
{
int heapsize = n;
while(heapsize > 1)
{
swap(a[heapsize--], a[1]);
repair_heap(a, 1, heapsize);
}
}
這一步複雜度爲 O(nlgn)。