Python實現優先佇列
阿新 • • 發佈:2019-02-14
Python有佇列類Queue,為啥就不提供個PriorityQueue類呢?
寫優先佇列也是在寫爬蟲的時候想到的,當時沒想用PageRank演算法(最終也沒用),就直接用優先佇列來放URL,但是發現Python沒有優先佇列。
網上我看到一哥們用Python的bisect包來實現優先佇列的
我們就來分析下他的優先佇列演算法複雜度吧,這僅僅是學術探討,沒有任何別的意思。
首先在元素插入佇列的時候,bisect的原理是用二分來搜尋需要插入的位置,然後將後面的元素平移一個位置,將該位置空出來給需要插入的元素
看bisect包的原始碼:
具體我不知道Python的list是怎麼個機制來平移的,但怎麼平移又要保證大小的順序不變,那麼複雜度也是O(n)吧。def insort_right(a, x, lo=0, hi=None): """Insert item x in list a, and keep it sorted assuming a is sorted. If x is already in a, insert it to the right of the rightmost x. Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. """ if lo < 0: raise ValueError('lo must be non-negative') if hi is None: hi = len(a) while lo < hi: mid = (lo+hi)//2 if x < a[mid]: hi = mid else: lo = mid+1 a.insert(lo, x) insort = insort_right
再次,當我們需要pop出一個元素的時候同樣他的方法是直接用list.pop(item),這樣也需要list自己來平移元素位置,複雜度也是O(n)
而實際上C++ STL中的優先佇列的插入和刪除的複雜度是O(logn)
對於Python list的機制我不瞭解,如果和C++中的陣列平移是一樣的話,那麼這種優先佇列的方法是不可取的。
那麼就需要自己寫堆了,說白了就是堆的Insert和Adjust兩個函式就搞定了
需要說明的是:此程式碼中我沒有使用list[0]這個位置,這樣再寫程式碼的時候比較直觀,我是這樣認為的,大家可以把root=0和root=1的兩種堆畫一畫就知道我說的了(子節點)
#-*-coding:utf-8-*- """ class PriorityQueue: """ class PriorityQueue: def __init__(self): self.queue = [] self.length = 0 self.queue.insert(0, self.length) #佇列中元素的個數 def count(self): return self.length #判斷佇列是否為空 def empty(self): if self.count() == 0: return True else : return False #取佇列首元素,但是此時沒有刪除該元素 def top(self): return self.queue[1] #刪除隊首元素 def pop(self): bEmpty = self.empty() if bEmpty == False: self.queue[1] = self.queue[self.length] self.length -= 1 self._adjust() #插入一個元素 def push(self,item): self.length += 1 self.queue.insert(self.length, item) #插入元素後對堆進行調整,使其滿足最大頂堆的性質 i = self.length while i >= 2 and self.queue[i][1] > self.queue[i/2][1]: self.queue[i] , self.queue[i/2] = self.queue[i/2] , self.queue[i] i = i / 2 #堆的調整函式 def _adjust(self): root = 1 j = root << 1 temp = self.queue[root] while j <= self.length: if j < self.length and self.queue[j][1] < self.queue[j+1][1]: j += 1 if self.queue[j] <= temp: break self.queue[j],self.queue[root] = self.queue[root],self.queue[j] root = j j = j << 1 self.queue[root] = temp if __name__ == '__main__': pq = PriorityQueue() pq.push(15) pq.push(8) pq.push(9) pq.push(3) pq.push(7) pq.push(6) print pq.queue while pq.empty() == False: print "Value = ",pq.top() print pq.queue,pq.length pq.pop()
程式碼我自己驗證過了,應該是沒有問題的