演算法 二叉堆

2022-07-04 15:21:07 字數 2698 閱讀 3002

實現棧或是佇列與實現優先佇列的最大不同在於對效能的要求。對於棧和佇列,我們的實現能夠在常數時間內完成所有操作;而對於優先佇列,插入元素和刪除最大元素這兩個操作之一在最壞情況下需要線性時間來完成。我們接下來要討論的基於資料結構堆的實現能夠保證這兩種操作都能更快地執行。 

1.堆的定義

資料結構二叉堆能夠很好地實現優先佇列的基本操作。在二叉堆的陣列中,每個元素都要保證大於等於另兩個特定位置的元素。相應地,這些位置的元素又至少要大於等於陣列中的另兩個元素,以此類推。如果我們將所有元素畫成一棵二叉樹,將每個較大元素和兩個較小的元素用邊連線就可以很容易看出這種結構。

定義:當一棵二叉樹的每個結點都大於等於它的兩個子結點時,它被稱為堆有序。

相應地,在堆有序的二叉樹中,每個結點都小於等於它的父結點(如果有的話)。從任意結點向上,我們都能得到一列非遞減的元素;從任意結點向下,我們都能得到一列非遞增的元素。

命題:根結點是堆有序的二叉樹中的最大結點

證明:根據樹的性質歸納可得

二叉堆表示法

如果我們用指標來表示堆有序的二叉樹,那麼每個元素都需要三個指標來找到它的上下結點(父結點和兩個子結點各需要乙個)。如下圖所示。完全二叉樹只用陣列而不需要指標就可以表示。具體方法就是將二叉樹的結點按照層級順序放入陣列中,根結點在位置1,它的子結點在位置2和3,而子結點的子結點則分別在位置4、5、6和7,以此類推。

定義:二叉堆是一組能夠用堆有序的完全二叉樹排序的元素,並在陣列中按照層級儲存(不使用陣列的第乙個位置)

簡單起見,在下文中我們將二叉堆簡稱為堆。在乙個堆中,位置k的結點的父結點的位置為└k/2┘,而它的兩個子結點的位置則分別為2k和2k+1。這樣在不使用指標的情況下我們也可以通過計算陣列的索引在樹中上下移動:從a[k]向上一層就令k等於k/2,向下一層則令k等於2k或2k+1.

用陣列(堆)實現的完全二叉樹的結構是很嚴格的,但它的靈活性已經足以讓我們高效地實現優先佇列。用它們我們將能實現對數級別的插入元素和刪除最大元素的操作。利用在陣列中無需指標即可沿樹上下移動的便利和以下性質,演算法保證了對數複雜度的效能。

命題:一棵大小為n的完全二叉樹的高度為└lgn┘

證明:通過歸納很容易可以證明這一點,且當n達到2的冪時樹的高度會加1

2.堆的演算法

堆的操作會首先進行一些簡單的改動,打破堆的狀態,然後再遍歷堆並按照要求將堆的狀態恢復。我們稱這個過程叫做堆的有序化。在有序化的過程中我們會遇到兩種情況。當某個結點的優先順序上公升(或是在堆底加入乙個新的元素)時,我們需要由下至上恢復堆的順序。當某個結點的優先順序下降(例如將根結點替換為乙個較小的元素)時,我們需要由上至下恢復堆的順序。首先,我們會學習如何實現這兩種輔助操作,然後再用它們實現插入元素和刪除最大元素的操作。

由下至上的堆有序化(上浮)

private void swim(int k) 

}

如果堆的有序狀態因為某個結點變得比它的父結點更大而被打破,那麼我們就需要通過交換它和它的父結點來修復堆。交換後,這個結點比它的兩個子結點都大(乙個是曾經的父結點,另乙個比它更小,因為它是曾經父結點的子結點),但這個結點仍然可能比它現在的父結點更大。我們可以一遍遍地用同樣的辦法恢復秩序,將這個結點不斷向上移動直到我們遇到了乙個更大的父結點。只要記住位置k的結點的父結點的位置是└k/2┘,這個過程實現起來很簡單。swim()方法中的迴圈可以保證只有位置k上的結點大於它的父結點時堆的有序狀態才會被打破。因此只要該結點不再大於它的父結點,堆的有序狀態就恢復了。如下圖

由上至下的堆有序化(下沉)

private void sink(int k) 

}

如果堆的有序狀態因為某個結點變得比它的兩個子結點或是其中之一更小了而被打破了,那麼我們可以通過將它和它的兩個子結點中的較大者交換來恢復堆。交換可能會在子結點處繼續打破堆的有序狀態,因此我們需要不斷地用相同的方式將其修復,將結點向下移動直到它的子結點都比它更小或是到達了堆的底部。由位置為k的結點的子結點位於2k和2k+1可以直接得到對應的**。

sink()和swim()方法是高效實現優先佇列api的基礎,原因如下。

插入元素。我們將新元素加到陣列末尾,增加堆的大小並讓這個新元素上浮到合適的位置(如下圖左半部分所示)。

刪除最大元素。我們從陣列頂端刪去最大的元素並將陣列的最後乙個元素放到頂端,減小堆的大小並讓這個元素下沉到合適的位置(如下圖右半部分所示)。

基於堆的優先佇列演算法解決了我們在開始時提出的乙個基本問題:它對優先佇列api的實現能夠保證插入元素和刪除最大元素這兩個操作的用時和佇列的大小僅成對數關係。

演算法 二叉堆

二叉堆具體來說就是支援插入刪除查詢最值的資料結構,是一棵滿足堆性質的完全二叉樹,樹上的每乙個節點對應乙個權值。若樹中的任意乙個節點的權值都小於其父節點的權值,則稱該二叉樹滿足大根堆性質,即我們常說的大根堆。若書中任意乙個節點的全職都打與其父親節點的權值,則稱該二叉樹滿足小根堆性質,即小根堆。二者統稱...

(二叉)堆操作

堆操作 實驗目的 一 建堆 將陣列a 1.n 變成乙個最大堆。二 堆排序 將乙個堆中的元素按遞減排序輸出。三 用插入方法建堆 堆大小從1到n每次插入乙個元素到堆中,直到n個元素入堆。實驗原理 二叉 堆是乙個陣列,它可以被看成乙個近似的完全二叉樹。樹上的每乙個結點對應陣列中的乙個元素。除了最底層外,該...

簡單 二叉堆

堆是一種比較有用的資料結構,是二叉樹的一種陣列的表示形式。最大堆和最小堆是二叉堆的兩種形式。最大堆 根結點的鍵值是所有堆結點鍵值中最大者。最小堆 根結點的鍵值是所有堆結點鍵值中最小者。而最大 最小堆集結了最大堆和最小堆的優點,這也是其名字的由來。最大 最小堆是最大層和最小層交替出現的二叉樹,即最大層...