優先隊列(堆)的定義和性質
定義:
優先隊列: 特殊的“隊列”,取出元素的順序是按照它的優先級大小,而不是元素進入隊列的先後順序。
使用堆來實現優先隊列。
性質:
- 結構性:用數組表示的完全二叉樹。
- 有序性:任一結點是其子樹所有結點的最大值(或最小值)。
最大堆:maxHeap,也叫大頂堆
最小堆:minHeap,也叫小頂堆 - 從根結點到任意結點的路徑上,都是有序的。
基礎操作
下面以大頂堆爲例進行說明
1.初始化
- 堆可以用數組實現
- 數組的第0個元素可以作爲“哨兵”,不存儲實際使用的值,用來簡化堆中的一些操作。比如存一個後面結點永遠比它小的值(MAX_VALUE),這樣可以方便後面的插入操作。
2. 插入
- 在數組後面插入新值
- 從新值從下往上進行調整,只要它比父結點大,父結點就往下移動;否則停止,將該值放到對應的位置
3.刪除堆頂元素(刪除最大值)
- 記錄堆頂元素。
- 將堆的最後一個元素放到堆頂。然後進行堆的調整。
- 從根結點開始,取它的左右子節點的最大值,看子節點是否比它大,如果子節點更大,將子節點往上移動,然後再看子節點的左右子節點,直到葉子節點或者左右子節點比該值小。
4.堆的構建
方式一: 循環插入 O(N*logN),將元素一個一個插入初始爲空的堆中。
方式二:首先將N個元素順序存入數組,先滿足完全二叉樹的性質;然後再從n/2的位置開始調整堆,使之符合有序性。每次調整都是 左右兩個子樹原本都是堆,然後將根結點進行調整。類似於刪除最大元素之後的調整。時間複雜度O(n)。
/*----------- 建造最大堆 -----------*/
void PercDown( MaxHeap H, int p )
{ /* 下濾:將H中以H->Data[p]爲根的子堆調整爲最大堆 */
int Parent, Child;
ElementType X;
X = H->Data[p]; /* 取出根結點存放的值 */
for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
Child = Parent * 2;
if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
Child++; /* Child指向左右子結點的較大者 */
if( X >= H->Data[Child] ) break; /* 找到了合適位置 */
else /* 下濾X */
H->Data[Parent] = H->Data[Child];
}
H->Data[Parent] = X;
}
void BuildHeap( MaxHeap H )
{ /* 調整H->Data[]中的元素,使滿足最大堆的有序性 */
/* 這裏假設所有H->Size個元素已經存在H->Data[]中 */
int i;
/* 從最後一個結點的父節點開始,到根結點1 */
for( i = H->Size/2; i>0; i-- )
PercDown( H, i );
}