本篇學習內容為堆的性質、python實現插入與刪除操作、堆復雜度表、python內置方法生成堆。
區分堆(heap)與棧(stack):堆與二叉樹有關,像一堆金字塔型泥沙;而棧像一個直立垃圾桶,一列下來。
堆(heap)
又被為優先隊列(priority queue)。盡管名為優先隊列,但堆並不是隊列。回憶一下,在隊列中,我們可以進行的限定操作是dequeue和enqueue。
dequeue是按照進入隊列的先后順序來取出元素。而在堆中,我們不是按照元素進入隊列的先后順序取出元素的,而是按照元素的優先級取出元素。
性質
堆的實現通過構造二叉堆(binary heap),實為二叉樹的一種;由於其應用的普遍性,當不加限定時,均指該數據結構的這種實現。這種數據結構具有以下性質。
- 任意節點小於(或大於)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)。
- 堆總是一棵完全樹。即除了最底層,其他層的節點都被元素填滿,且最底層盡可能地從左到右填入。
實現
- 堆的主要操作是插入和刪除最小元素(元素值本身為優先級鍵值,小元素享有高優先級)
- 在插入或者刪除操作之后,我們必須保持該實現應有的性質: 1. 完全二叉樹 2. 每個節點值都小於或等於它的子節點
上浮(Promotion)
情境: 子節點的鍵值變為比父節點的鍵值大;如下面添加字節點
消除這種違反項:
- 交換子節點的鍵和父節點的鍵
- 重復這個過程直到堆的順序恢復正常
堆的添加:
def _upheap(self, j):#往上交換 parent = self._parent(j) if j > 0 and self._data[j] < self._data[parent]: self._swap(j, parent) self._upheap(parent)
下沉(Demotion)
情境:父節點的鍵值變得比子節點(一個或者2個) 的鍵值還小 ,如下面刪除了根節點后拿了個小子節點補充上來的情況
消除這種違反項:
- 把父節點的鍵值和比它大的子節點的鍵值做交換
- 重復這個操作直到堆的順序恢復正常
刪除最大值
def _downheap(self, j):#往下交換,遞歸比較三個值 if self._has_left(j): left = self._left(j) small_child = left if self._has_right(j): right = self._right(j) if self._data[right] < self._data[left]: small_child = right if self._data[small_child] < self._data[j]: self._swap(j, small_child) self._downheap(small_child)
復雜度分析
Python構建堆的代碼:
#該heap為min_heap,即根節點為最小值 class PriorityQueueBase: #抽象基類為堆 class Item: #輕量級組合來存儲堆項目 __slots__ = '_key' , '_value' def __init__ (self, k, v): self._key = k self._value = v def __lt__ (self, other): #比較大小 return self._key < other._key def is_empty(self): return len(self) == 0 def __str__(self): return str(self._key) class HeapPriorityQueue(PriorityQueueBase): def __init__ (self): self._data = [ ] def __len__ (self): return len(self._data) def is_empty(self): return len(self) == 0 def add(self, key, value): #在后面加上然后加上 self._data.append(self.Item(key, value)) self._upheap(len(self._data) - 1) def min(self): if self.is_empty(): raise ValueError( "Priority queue is empty." ) item = self._data[0] return (item._key, item._value) def remove_min(self): if self.is_empty(): raise ValueError( "Priority queue is empty." ) self._swap(0, len(self._data) - 1) item = self._data.pop( ) self._downheap(0) return (item._key, item._value) def _parent(self, j): return (j - 1) // 2 def _left(self, j): return 2 * j + 1 def _right(self, j): return 2 * j + 2 def _has_left(self, j): return self._left(j) < len(self._data) def _has_right(self, j): return self._right(j) < len(self._data) def _swap(self, i, j): self._data[i], self._data[j] = self._data[j], self._data[i] def _upheap(self, j):#往上交換 parent = self._parent(j) if j > 0 and self._data[j] < self._data[parent]: self._swap(j, parent) self._upheap(parent) def _downheap(self, j):#往下交換,遞歸比較三個值 if self._has_left(j): left = self._left(j) small_child = left if self._has_right(j): right = self._right(j) if self._data[right] < self._data[left]: small_child = right if self._data[small_child] < self._data[j]: self._swap(j, small_child) self._downheap(small_child) heap = HeapPriorityQueue() heap.add(4, "D") heap.add(3, "C") heap.add(1, "A") heap.add(5, "E") heap.add(2, "B") heap.add(7, "G") heap.add(6, "F") heap.add(26, "Z") for item in heap._data: print(item) print("min is: ") print(heap.min()) print() print("remove min: ") print(heap.remove_min()) print("Now min is: ") print(heap.min()) print() print("remove min: ") print(heap.remove_min()) print("Now min is: ") print(heap.min()) print() heap.add(1, "A") print("Now min is: ") print(heap.min()) print() #輸出結果 1 2 3 5 4 7 6 26 min is: (1, 'A') remove min: (1, 'A') Now min is: (2, 'B') remove min: (2, 'B') Now min is: (3, 'C') Now min is: (1, 'A')
python內置方法創建堆有兩種方式,heappush()和heapify()
''' heaqp模塊提供了堆隊列算法的實現,也稱為優先級隊列算法。 要創建堆,請使用初始化為[]的列表,或者可以通過函數heapify()將填充列表轉換為堆。 提供以下功能: heapq.heappush(堆,項目) 將值項推入堆中,保持堆不變。 heapq.heapify(x) 在線性時間內將列表x轉換為堆。 heapq.heappop(堆) 彈出並返回堆中的最小項,保持堆不變。如果堆是空的,則引發IndexError。 ''' import heapq #1 heappush生成堆+ heappop把堆從小到大pop出來 heap = [] data = [1,3,5,7,9,2,4,6,8,0] for i in data: heapq.heappush(heap,i) print(heap) lis = [] while heap: lis.append(heapq.heappop(heap)) print(lis) #2 heapify生成堆+ heappop把堆從小到大pop出來 data2 = [1,5,3,2,9,5] heapq.heapify(data2) print(data2) lis2 = [] while data2: lis2.append(heapq.heappop(data2)) print(lis2) #輸出結果 [0, 1, 2, 6, 3, 5, 4, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [1, 2, 3, 5, 9, 5] [1, 2, 3, 5, 5, 9]