1. 程式人生 > >Python實現優先佇列

Python實現優先佇列

Python有佇列類Queue,為啥就不提供個PriorityQueue類呢?

寫優先佇列也是在寫爬蟲的時候想到的,當時沒想用PageRank演算法(最終也沒用),就直接用優先佇列來放URL,但是發現Python沒有優先佇列。

網上我看到一哥們用Python的bisect包來實現優先佇列的

我們就來分析下他的優先佇列演算法複雜度吧,這僅僅是學術探討,沒有任何別的意思。

首先在元素插入佇列的時候,bisect的原理是用二分來搜尋需要插入的位置,然後將後面的元素平移一個位置,將該位置空出來給需要插入的元素

看bisect包的原始碼:

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 
具體我不知道Python的list是怎麼個機制來平移的,但怎麼平移又要保證大小的順序不變,那麼複雜度也是O(n)吧。

再次,當我們需要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()

程式碼我自己驗證過了,應該是沒有問題的