堆是一顆完全二叉樹,樹中每個結點的值都不小於(或不大於)其左右孩子結點的值,其中,如果父親結點的值大於或等於孩子結點的值,那麼稱這樣的堆為大頂堆,這時每個結點的值都是以它為根結點的子樹的最大值;如果父親結點的值小於或等於孩子結點的值,那麼稱這樣的堆為小頂堆,這時每個節點的值都是以它為根結點的子樹的最小值。堆一般用於優先佇列的實現,而優先佇列的實現預設情況下使用的是大頂堆。
那麼到底如何來建立堆呢?對完全二叉樹來說,比較簡潔的實現方法是用陣列來儲存完全二叉樹,這樣結點就按照層序儲存於陣列中,其中第乙個結點將儲存於陣列中的1號位,並且陣列i號位表示的結點的左孩子就是2i號位,而右孩子則是(2i+1)位。於是可以像下面這樣定義陣列來表示堆。
const int maxn=100;
//heap為堆,n為元素個數
int heap[maxn],n=10;
在建堆的過程中,每次調整都是把結點從上往下的調整。針對這種向下調整,調整方法是這樣的:總是將當前結點v與它的左右孩子比較(如果有的話),假如孩子中存在權值比結點v的權值大的,就將其中權值最大的那個孩子節點與結點v交換;交換完畢後繼續讓結點v和孩子比較,直到結點v的孩子的權值都比結點v的權值小或是結點v不存在孩子結點。
於是可以很容易的寫出向下調整的**,顯然時間複雜度為o(logn)
//對heap陣列在[low,high]範圍內進行調整
//其中low為欲調整結點的陣列下標,high一般為堆的最後乙個元素的陣列下標
void downadjust(int low,int high)
//如果孩子中最大的權值比欲調整結點i大
if(heap[j]>heap[i])else
} }
那麼建堆的過程也就很容易了。假設序列中元素的個數為n,由於完全二叉樹的葉子結點個數為[n/2],因此陣列下標在[1,[[n/2]]範圍內的結點都是非葉子節點,於是可以從[n/2]號為開始倒著列舉結點,對每個遍歷到的結點i進行[i,n]範圍的調整。為什麼要倒著列舉呢?這是因為每次調整完乙個結點後,當前子樹中權值最大的結點就會處在根結點的位置,這樣當遍歷到其父親結點時,就可以直接使用這個結果,也就是說,這種做法保證每個結點都是以其為根結點的子樹中權值最大的結點。
建堆的**如下,時間複雜度為o(n)
//建堆
void createheap()
}
另外,如果要刪除堆中的最大元素(也就是刪除堆頂元素),並讓其仍然保持堆的結構,那麼只需要最後乙個元素覆蓋堆頂元素,然後對根結點進行調整即可。**如下,時間複雜度為o(logn)'
//刪除堆頂元素
void deletetop()
那麼,如果想要往堆裡新增乙個元素,可以把想要新增的元素放在陣列最後(也就是完全二叉樹的最後乙個結點後面),然後進行向上調整操作。向上調整總是把欲調整結點與父親結點比較,如果權值比父親結點大,那麼就交換其與父親結點,這樣反覆比較,直到堆頂或是父親結點的權值較大為止。向上調整的**如下,時間複雜度為o(logn)
//對heap陣列在[low,high]範圍進行向上調整
//其中low一般設定為1,high表示欲調整的結點的陣列下標
void upadjust(int low,int high)
堆的基本操作
堆的基本概念 如果有乙個關鍵碼的集合k 把它的所有元素按完全二叉樹的順序儲存方式儲存在乙個一維陣列中,並滿足 ki k2 i 1 且 ki k2 i 2 ki k2 i 1 且 ki k2 i 2 i 0,1,2 則稱為小堆 或大堆 如下圖 分別為小堆和大堆 堆的操作具體 如下 測試環境 vs201...
堆的基本操作
堆的資料結構 對於堆,有最大堆和最小堆,在定義乙個堆的時候用乙個陣列表示堆,同時為了方便定義堆的大小,用乙個 size 表示堆的有效元素,同時為了區別最大堆和最小堆,我們用乙個函式指標表示這個堆是最大堆還是最小堆.typedef int compare heaptype parent,heaptyp...
堆的基本操作
heap.h ifndef heap h define heap h 定義乙個函式指標 typedef int pf hdatatype left,hdatatype right typedef int hdatatype typedef struct heap heap int greater h...