Python 堆的實現 heapq PriorityQueue


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
View Code

 

 

2、自己實現

待續


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM