堆 heap 的定義及其演算法分析

2021-08-26 10:55:11 字數 2373 閱讀 8713

堆(heap)是與二叉查詢樹類似的adt。但又不同於二叉查詢樹,主要體現在兩個方面。第一,可將二叉查詢樹看著是有序的,而堆是有序的,這一概念較弱。不過,為使優先佇列操作有效執行,這完全滿足要求。第二,二叉查詢樹有多種不同形狀,而堆總是完全二叉樹。

堆是完全二叉樹,可以為空,或者:

(1)根包含的查詢關鍵字大於或等於各個孩子的查詢關鍵字。

(2)根包含作為子樹的堆。

在這個堆的定義中,根項包含的查詢關鍵字最大。這樣的堆稱為maxheap。相對而言,minheap將包含最小查詢關鍵字的項放在根中。

堆是完全二叉樹,因此,若了解堆的最大規模,則可以用二叉樹的基於陣列的實現。圖1顯示堆及其陣列表示。堆節點中的查詢關鍵字大於或等於節點的孩子的查詢關鍵字。另外,在堆中,孩子的查詢關鍵字相互無關:換言之,並不知道那個孩子包含的查詢關鍵字更大。

圖1 堆及陣列表示形式

堆的基於陣列的實現

* items :堆項陣列

* size :表示堆中的項數的整數

陣列items對應於樹的基於陣列的表示。在下面的討論中,為簡化起見,假設堆項是整數。

heapdelete

首先考慮heapdelete操作。堆中最大的查詢關鍵字在什麼位置?因為各個節點的查詢關鍵字都大於或等於任意乙個孩子的查詢關鍵字,所以最大的查詢關鍵字一定在樹根中,這樣,heapdelete操作的第1步為:

// return the item in the root

rootitem=item[0];

這很簡單,但也必須刪除根。再刪除根後,將留下兩個分離的堆,如圖2-a所示。因此,需要將其餘點在轉化為乙個堆。在開始轉換的時候,取出樹中最後乙個節點的項,並將其放在根中,如下所示:

//copy the item from the last node into the root

item[0]=item[size-1];

//remove the last node

--size

如圖2-b所示,給步驟的結果不一定是堆,不過,這是一棵完全二叉樹,左右子樹都是堆。唯一的問題是,根項可能不在正確的位置,這樣的結構稱為半堆(semiheap)。需要一種方式,將半堆轉化為堆。一種策略是使根項逐漸下移,直到進入處於正確的位置的節點停止。為此,首先比較半堆根的查詢關鍵字與孩子的查詢關鍵字。如果根的查詢關鍵字小於孩子的查詢關鍵字中的比較大者,則交換根與較大孩子的項,所謂較大孩子,即查詢關鍵字大於另乙個孩子的查詢關鍵字。

圖2 (a)分離堆 (b)半堆

圖3顯示heapdelete操作。只做了一次交換,值5就下移至正確的位置:不過,一般情況都需要多次交換。實際上,一旦交換了根項和較大孩子c的項,c就成為半堆的根(注意,c並不移動,更改的是它的值)。該策略引出了下面的迭歸演算法。

圖3 從堆中刪除

圖4演示heaprebuild 的遞迴呼叫。

圖4 heaprebuild的遞迴呼叫。

heapdelete操作使用heaprebuilt。如下所示:

//return the item from the last node into the root

rootitem=item[0];

//copy the item from the last node into the root

items[0]=items[size-1];

//remove the last node

--size;

//transform the semiheap back into a heap

heaprebuild(items,0,size);

簡要分析heapdelete的效率。樹儲存在陣列中,為刪除節點,需要交換陣列元素,而不是簡單的修改幾個指標。這些交換值得關注,但不意味著效率低下。最多交換陣列元素多少次?在heapdelete將樹中最後乙個節點的項複製到根之後,heaprebuilt在樹中下移此項,直到適當的位置,在最壞的情況下,項從根出發,沿單個路徑下移,到達葉子。因此,heaprebuilt必須交換的陣列項數不大於樹的高度。包含n個節點的完全二叉樹的高度為[log2(n+1)](大於log2(n+1)的最小整數)。每個交換需要移動3次資料。因此,heapdelete需要的資料移動次數為3*[log2(n+1)]+1所以,heapdelete為o(log n),實際上,這非常有效。

heapinsert

heapinsert 演算法的策略與heapdelete正好相反。在樹低插入乙個新項,然後上移到適當位置,如圖5所示。上移節點很容易,因為items[i](而不是根)節點總儲存在items[(i-1)/2]中。heapinsert操作的偽碼如下:

圖5 在堆中插入

heapinsert的效率與heapdelete相同,換言之,在最壞的情況下,heapinset必須交換從葉子到根的路徑上的陣列元素。故交換次數不超過樹高。完全二叉樹的高度為[log2(n+1)],因此heapinsert也是o(log n)。

堆 heap 排序演算法

堆排序演算法是複雜的排序演算法,是不穩定的排序演算法。1 堆排序的基本思想 堆排序定義 n個有序列a1,a2,an成為堆,有下面兩種不同型別的堆。大根堆 所有子節點都大於其父節點,即ai a2i且ai a2i 1。小根堆 所有子節點都小於其父節點,即ai a2i且ai a2i 1。若將此序列所儲存的...

STL中heap演算法(堆演算法)

push heap演算法 以下是push heap演算法的實現細節。該函式接收兩個迭代器,用來表現乙個heap底部容器 vector 的頭尾,而且新元素已經插入究竟部的最尾端。template inline void push heap randomaccessiterator first,rand...

堆(Heap)的實現

這次實現了堆,這個堆不是指系統堆疊的堆,是一種資料結構,見下圖 堆的本質就是乙個陣列 上圖中,紅色的是值,黑色的是下標 簡單的來說就是把乙個陣列看成是二叉樹,就像上圖 大堆和小堆分別是指根節點比孩子節點的值大或者是小,看了上圖之後就可以發現,父親節點和孩子節點之間下表的關係,parnet child...