給出乙個有n個元素的陣列a[1...n],要建立乙個包含這些元素的堆,可以這樣進行:從空的堆開始,不斷插入每乙個元素,直到a完全被轉移到堆中為止。因為插入第j個鍵值用時o(log j),因此用這種方法建立堆疊的時間複雜性是o(n log n)。
我們知道對應於堆h[1...n]的樹的節點可以方便地以自頂向下、從左到右的方式從1到n編碼。在這樣編碼之後,可以用下面的方法,把一棵n個節點的幾乎完全的二叉樹轉換成堆h[1...n]。從最後乙個節點開始(編碼為n的那乙個)到根節點(編碼為1的節點),逐個掃瞄所有的節點,根據需要,每一次將以當前節點為根節點的子樹轉換成堆。
每一棵只有一片葉子的子樹已經是乙個堆,因此葉子被跳過。
當以第i個節點為根的子樹不是堆時,我們就對i進行sift-down運算一邊把它轉換成堆。如此進行下去,使整棵二叉樹符合堆得性質。
上面說明了如何對樹進行運算。直接對輸入的陣列執行同樣的過程是相當容易的。
令a[1...n]是已知陣列,t是對應於a的一棵幾乎完全的二叉樹,我們注意到 a[⌊
n/2⌋
+1],a[⌊
n/2⌋
+2],...,a[n] 它們對應於t的葉子,這樣我們可以直接從a[⌊
n/2⌋
]開始調整陣列,然後繼續調整 a[⌊
n/2⌋
-1],a[⌊
n/2⌋
-2],...,a[1]。這樣得到的陣列就是我們需要的堆。
過程 makeheap
輸入 n個元素的陣列a[1...n]
輸出 a[1...n]轉換成堆
演算法描述
fori ← ⌊
n/2⌋
downto1
sift-down(a, i)
endfor
注意這裡演算法描述的陣列的索引都是1...n,而不是大家習慣的0...n-1。
下面我們來計算演算法makeheap的執行時間
設t是對應於陣列a[1...n]的一棵幾乎完全的二叉樹,那麼由觀察結論可知,t的高是k=⌊log n
⌋。令a[j]對應該樹的第i層中第j個節點,當語句sift-down(a, j)呼叫過程sift-down時,重複執行的次數最多是k-i。
因為在第i層上正好有2i個節點,0≤i<k,所以每一層迴圈執行的總次數的上界是 (k-i)2i。
總的迴圈執行的總次數的上界就是:
由於在過程sift-down的每乙個迴圈中,最多有兩次元素的比較,因此元素比較的總次數是2×2n=4n。
但是在每次呼叫sift-down時,都至少執行一次迴圈,因此元素比較的最小次數是2⌊
n/2⌋
≥n-1,這個就是元素比較的總次數的下界。
因此,makeheap演算法需要θ(n)時間來構造乙個n元素的堆,似乎並不是之前我們認為的o(n log n)時間。
int* makeheap(int* array, int arraylength)
for (int i = heaplength / 2; i >= 1; i--)
return heap;
}
我使用的陣列是這樣定義的:
const int array_length = 10;
int array[array_length] = ;
// 如果用我上面寫的方法建立堆,不要忘了在操作完成之後釋放記憶體哦
int* heap = makeheap(array, array_length);
// do something ...
free(heap);
演算法 之 堆 - 簡介
演算法 之 堆 - sift-up和sift-down
演算法 之 堆 - 插入和刪除
堆python 建立堆 python
建立最大 小 堆 二叉堆本質上是一種完全二叉樹,儲存方式並不是鏈式儲存,而是順序儲存 堆操作 插入 葉子節點上調 刪除 堆頂元素下沉 堆建立 非葉子節點下沉 從最後乙個非葉子節點開始 最小堆 最小堆任何乙個父節點的值,都小於等於它左右孩子節點的值 建立過程 如果非葉子節點值大於其子節點,將其下沉 最...
演算法 之 堆 小結
之前我們討論的堆,都把最大鍵值儲存在根節點中,這種型別的堆可以看作是最大堆。我們還可以建立另一種堆,把最鍵小值儲存在根節點中,根節點以外的節點的鍵值,大於或等於儲存在它父節點中的鍵值,這種型別的堆可以看作是最小堆。具體是用最大堆還是最小堆,就根據我們自身的需要來選擇了。堆的演算法到這裡就講完了,下面...
堆 大根堆和小根堆的建立
堆是一種經過排序的完全二叉樹,其中任一非終端節點的資料值均不大於 或不小於 其左孩子和右孩子節點的值。1 根結點 亦稱為堆頂 的關鍵字是堆裡所有結點關鍵字中最小者的堆稱為小根堆。1 根結點 亦稱為堆頂 的關鍵字是堆裡所有結點關鍵字中最大者,稱為大根堆。用堆的關鍵部分是兩個操作 1 put操作 即往堆...