插入-插入元素索引上移,父節點值下移;
#include <stdlib.h> #include <stdIo.h> typedef int ElementType; typedef struct HNode *Heap; /* 堆的型別定義 */ struct HNode { ElementType *Data; /* 儲存元素的陣列 */ int Size; /* 堆中當前元素個數 */ int Capacity; /* 堆的最大容量 */ }; typedef Heap MaxHeap; /* 最大堆 */ #define MAXDATA 1000000 /* 該值應根據具體情況定義為大於堆中所有可能元素的值 */
MaxHeap InitializeHeap( int MaxSize ) { /* 建立容量為MaxSize的空的最大堆 */ MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode)); /* 多一個元素存放"哨兵" */ H->Data = (ElementType *)malloc((MaxSize+1)*sizeof(ElementType)); H->Size = 0; H->Capacity = MaxSize; H->Data[0] = MAXDATA; /* 定義"哨兵"為大於堆中所有可能元素的值*/ return H; }
判是否滿堆,以及是否為空
bool IsFull( MaxHeap H ) { return (H->Size == H->Capacity); } bool IsEmpty( MaxHeap H ) { return (H->Size == 0); }
將新增結點插入到,從其父結點到根結點的有序序列中 ( 完全二元樹,插入時間複雜度O(logN) )
void Insert( MaxHeap H, ElementType X ) { /* 將元素X插入最大堆H,其中H->Data[0]已經定義為哨兵 */ int i; /* 首先判斷,堆是否已滿。已滿則結束 */ if ( IsFull(H) ) { printf("最大堆已滿"); return; } /* 若堆未滿,i指向堆末尾的下一個位置(空穴,當前size+1),準備插入X */ i = ++H->Size; /* 類似插入排序, */ /* 若X 大於 其父節點值,則將父節點值下移至位置i, i位置(空穴)移到父節點位置[i/2] */ for ( ; H->Data[i/2] < X; i /= 2 ) H->Data[i] = H->Data[i/2]; /* 上濾X */ H->Data[i] = X; /* 將X插入 */ /* 若X是當前堆中最大元素,那麼會在堆頂時(比哨兵小)終止上移 */ }
刪除位置-根結點,返回堆頂(最大值)元素,並調整堆使其保持堆序性(少了一個元素)。
ElementType DeleteMax( MaxHeap H ) { /* 從最大堆H中取出鍵值為最大的元素,並刪除一個結點 */ int Parent, Child; /* 指標 */ ElementType MaxItem, X; if ( IsEmpty(H) ) { printf("最大堆已為空"); /* 若堆已空,則結束(沒得刪) */ return ERROR; } MaxItem = H->Data[1]; /* 取出根結點存放的最大值 */ /* 用最大堆中最後一個元素X,從根結點開始,向上過濾下層結點 */ X = H->Data[H->Size--]; /* 相當於刪掉末尾元素位置,故當前堆size要減1*/ /* 迭代地將X和其更大的孩子節點值作比較,並調整位置(從根節點開始,給X找個位置) */ /* Parent*2 <= H->Size判斷是否有左兒子(有無孩子),若無則超出堆空間,跳出迴圈,直接把X放Parent */ for ( Parent = 1; Parent*2 <= H->Size; Parent = Child ) { /* 找到當前更大的孩子節點*/ Child = Parent * 2; /* 令Child為左兒子,經過外層for迴圈判斷,Child只能 <= Parent */ /* 若有右兒子((Child < H->Size)),則讓讓Child指向左右子結點的較大者 */ if ( (Child != H->Size) && (H->Data[Child] < H->Data[Child+1]) ) Child++; /* 將末尾元素X和Child的值比較,若X >= Child值則結束(有序了)*/ /* 若X < Child值 (Child更大),則將Child值放在位置Parent,並將Parent位置移到Child位置 */ if ( X >= H->Data[Child] ) break; /* 找到了合適位置 */ else /* Child元素上移,X移動到下一層(Parent = Child),繼續和其孩子節點比較 */ H->Data[Parent] = H->Data[Child]; } H->Data[Parent] = X; return MaxItem; }
void PercolateDown( MaxHeap H, int p ) { /* 下濾:將H中以H->Data[p]為根的子堆調整為最大堆 */ int Parent, Child; ElementType 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-- ) PercolateDown( H, i ); }
分析
ElementType DeleteMax( MaxHeap H ) { /* 從最大堆H中取出鍵值為最大的元素,並刪除一個結點 */ ElementType MaxItem = H->Data[1]; /* 取出根結點存放的最大值 */ H->Data[1] = H->Data[H->Size--] /* 取出根結點存放的最大值 */ PercolateDown(H, 1); /* 從根結點開始,向上過濾下層結點(末尾節點下濾) */ return MaxItem; }
邏輯參照上述C語言版
class Heap: def __init__(self, n): self.capacity = n self.size = 0 self.arr = [None] * (self.capacity+1) self.arr[0] = 2e24 def insert(self, num): if self.size == self.capacity: print("Out of size") else: self.size += 1 child = self.size # 空穴位置 # 上濾, 當左兒子在堆範圍內 while num > self.arr[child // 2]: parent = child // 2 self.arr[child] = self.arr[parent] child = parent self.arr[child] = num def pop(self): if self.size == 0: print("Empty") else: max_item = self.arr[1] # 取堆頂 x = self.arr[self.size] # 取堆末尾元素 self.size -= 1 parent = 1 # 下濾, 當左兒子在堆範圍內 while parent * 2 <= self.size: child = parent * 2 if child != self.size and self.arr[child+1] > self.arr[child]: child += 1 if self.arr[child] > x: self.arr[parent] = self.arr[child] # 孩子節點值上移 parent = child else: break self.arr[parent] = x return max_item
呼叫python包
import queue , random class Heap(): def __init__(self, k): if k > 0: self.q = queue.PriorityQueue(k) def queue(self): return self.q.queue def enque(self, key): # 當前堆大小小於其容量 if self.q._qsize() < self.q.maxsize: self.q.put(key) else: self.q.get() # 刪除堆頂 self.q.put(key) def deque(self): if not self.q.empty(): return self.q.get() else: print("Empty heap") h1 = Heap(10) for i in range(15): h1.enque(i) print(h1.queue()) # 最小堆,k 可得到堆排序得到最大的k個 l1 = [ random.randint(1, 100) for i in range(20)] print(l1) for i in l1: h1.enque(i) print(h1.queue()) print("\nPriority Queue:") print([h1.deque() for i in range(h1.q._qsize())])
經典的應用有選擇問題、堆排序和Huffman編碼等等。
def sortArray(nums: List[int]) -> List[int]: import heapq heapq.heapify(nums) return [heapq.heappop(nums) for i in range(len(nums))]
Python實現
class Solution: def sortArray(self, nums: List[int]) -> List[int]: def heapify(nums, parent, arr_size): # parent為開始下濾節點索引,p為當前堆大小(決定調整邊界) x = nums[parent] # 下濾, 當左兒子在堆範圍內 while parent * 2 + 1 < arr_size: child = parent * 2 + 1 if child != arr_size-1 and nums[child+1] > nums[child]: child += 1 if nums[child] > x: nums[parent] = nums[child] parent = child else: break nums[parent] = x # 構建堆 n = len(nums) for i in range(n//2, -1, -1): heapify(nums, i, n) # 建堆時堆大小固定為其容量 # 迭代刪除堆頂元素 for i in range(n-1, 0, -1): # 將堆頂元素取出(直接在末尾儲存),把末尾元素放堆頂 nums[i], nums[0] = nums[0], nums[i] heapify(nums, 0, i) # 然後下濾 return nums
C實現
void PercolateDown( ElementType A[], int p, int N ) { /* 將N個元素的陣列中以A[p]為根的子堆調整為最大堆 */ int Parent, Child; ElementType X = A[p]; /* 取出根結點存放的值 */ for ( Parent=p; (Parent*2+1) < N; Parent=Child ) { Child = Parent * 2 + 1; if ( (Child != N-1) && (A[Child] < A[Child+1]) ) Child++; /* Child指向左右子結點的較大者 */ if ( X >= A[Child] ) break; /* 找到了合適位置 */ else /* 下濾X */ A[Parent] = A[Child]; } A[Parent] = X; } void HeapSort( ElementType A[], int N ) { int i; /* 建立最大堆 */ for ( i = N/2-1; i >= 0; i-- ) PercolateDown( A, i, N ); for ( i=N-1; i>0; i-- ) { /* 刪除最大堆頂 */ Swap( &A[0], &A[i] ); PercolateDown( A, 0, i ); } }