1、系統實現
堆(heap),一種數據結構,它是一種優先隊列。優先隊列讓你能夠以任意順序添加對象,並隨時(可能是在兩次添加對象之間)找出(並刪除)最小的元素。相比於列表方法min,這樣做的效率要高得多。
1.1 heapq
實際上,Python沒有獨立的堆類型,而只有一個包含一些堆操作函數的模塊。這個模塊名為heapq(其中的q表示隊列),它包含6個函數,其中前4個與堆操作直接相關。必須使用列表來表示堆對象本身。
模塊heapq中一些重要的函數
- heappush(heap, x) 將x壓入堆中
- heappop(heap) 從堆中彈出最小的元素
- heapify(heap) 讓列表具備堆特征
- heapreplace(heap, x) 彈出最小的元素,並將x壓入堆中
- nlargest(n, iter) 返回iter中n個最大的元素
- nsmallest(n, iter) 返回iter中n個最小的元素
栗子:
1 from heapq import * 2 3 arr = [10, 20, 30, 40, 2, 8, 6, 4, 3, 3, 3, 1, 7, 5, 60] 4 heap = [] 5 for i in range(len(arr)): 6 heappush(heap, arr[i]) 7 print("heap=", heap) 8 9 print("順序為:", end="") 10 while heap: 11 print(heappop(heap), end=" ") 12 13 print("\n###########################################") 14 15 heapify(arr) 16 print(arr) 17 print("順序為:", end="") 18 while arr: 19 print(heappop(arr), end=" ")
結果如下:
heap= [1, 3, 2, 4, 3, 6, 5, 40, 10, 20, 3, 30, 7, 8, 60]
順序為:1 2 3 3 3 4 5 6 7 8 10 20 30 40 60
###########################################
[1, 2, 5, 3, 3, 7, 6, 4, 40, 3, 20, 8, 30, 10, 60]
順序為:1 2 3 3 3 4 5 6 7 8 10 20 30 40 60
可以看到,使用heapify和heappush方法,heap的結果是不一樣的,注意區分
注意:
heapq的實現是小根堆。如果要實現大根堆,將入棧的元素變為相反數即可
1 from heapq import * 2 3 arr = [10, 20, 30, 40, 2, 8, 6, 4, 3, 3, 3, 1, 7, 5, 60] 4 heap = [] 5 for i in range(len(arr)): 6 heappush(heap, -arr[i]) 7 print("heap=", heap) 8 9 print("順序為:", end="") 10 while heap: 11 print(heappop(heap)*(-1), end=" ")
heap= [-60, -30, -40, -10, -3, -8, -20, -4, -3, -2, -3, -1, -7, -5, -6]
順序為:60 40 30 20 10 8 7 6 5 4 3 3 3 2 1
1.2 priority_queue
queue.PriorityQueue這個優先級隊列的實現在內部使用了heapq,時間和空間復雜度與heapq相同。
區別在於PriorityQueue是同步的,提供了鎖語義來支持多個並發的生產者和消費者。在不同情況下,鎖語義可能會帶來幫助,也可能會導致不必要的開銷。
調用方法:
from queue import PriorityQueue
最常用的成員函數
put()
. get()
取隊首元素的值並將其彈出. 當優先隊列為空時,調用get()
不會報錯,而是會一直等待取出元素,可能和 PriorityQueue 支持並發的特性有關.
full()
判斷是否為滿. empty()
判斷是否為空.
1 from queue import PriorityQueue 2 3 q = PriorityQueue() 4 5 q.put((2, 'code')) 6 q.put((1, 'eat')) 7 q.put((3, 'sleep')) 8 9 while not q.empty(): 10 next_item = q.get() 11 print(next_item) 12 13 # 結果: 14 # (1, 'eat') 15 # (2, 'code') 16 # (3, 'sleep')
同樣的,python中的priority_queue如何從大到小排?
其實解法很簡單,優先隊列在插入元素的時候已經對元素做了排序,把最小的元素放在隊尾,那么只要在插入的時候,讓最大值被判斷為最小就好了。
1 items = [3,1,2] 2 pq = PriorityQueue() 3 for element in items: 4 pq.put((-element,element)) 5 print (pq.get()[1])
Python在對tuple類型作比較時,采用的是按照元素順序,找到第一個可比較的元素進行比較。
源碼如下:

1 class PriorityQueue(Queue): 2 '''Variant of Queue that retrieves open entries in priority order (lowest first). 3 4 Entries are typically tuples of the form: (priority number, data). 5 ''' 6 7 def _init(self, maxsize): 8 self.queue = [] 9 10 def _qsize(self): 11 return len(self.queue) 12 13 def _put(self, item): 14 heappush(self.queue, item) 15 16 def _get(self): 17 return heappop(self.queue) 18 19 20 21 22 def heappush(heap, item): 23 """Push item onto heap, maintaining the heap invariant.""" 24 heap.append(item) 25 _siftdown(heap, 0, len(heap)-1) 26 27 28 29 30 # 'heap' is a heap at all indices >= startpos, except possibly for pos. pos 31 # is the index of a leaf with a possibly out-of-order value. Restore the 32 # heap invariant. 33 def _siftdown(heap, startpos, pos): 34 newitem = heap[pos] 35 # Follow the path to the root, moving parents down until finding a place 36 # newitem fits. 37 while pos > startpos: 38 parentpos = (pos - 1) >> 1 39 parent = heap[parentpos] 40 if newitem < parent: 41 heap[pos] = parent 42 pos = parentpos 43 continue 44 break 45 heap[pos] = newitem
2、自己實現
待續