1.5 實現優先級隊列
問題:
實現一個隊列,能夠按照給定的優先級排序,並且每次pop操作時都可以返回優先級最高的那個元素.
解決方案
1.import heapq
2.class PriorityQueue:
3. def __init__(self):
4. self._queue=[]
5. self._index=0
6. def push(self,item,priority):
7. heapq.heappush(self._queue,(-priority,self._index,item))
8. self._index +=1
9. def pop(self):
10. return heapq.heappop(self._queue)[-1]
11.
12.
13.class Item:
14. def __init__(self,name):
15. self.name = name
16. def __repr__(self):
17. return 'Item({!r})'.format(self.name)
18.
19.q=PriorityQueue()
20.q.push(Item('foo'),3)
21.q.push(Item('bar'),4)
22.q.push(Item('spam'),3)
23.
24.print(q.pop())
25.print(q.pop())
26.print(q.pop())
討論
- 這段代碼的核心在於heapq中的push和pop操作. push和pop的複雜度爲O(logN)總的來說,效率相對較高.
- heapq是一個最小堆. 也就是說,堆中的對象必須是可以比較大小的. 而Item的實例並不能比較大小. 所以我們可以構造一個元組(priority,item)但是這樣做不到所有數據的唯一性. 因此,再加一個index
知識補充
heapq模塊
- heap是一個二叉樹結構,初始化爲一個空列表. 其父節點永遠比子節點小.heap[k] <= heap[2*k+1] and heap[k] <= heap[2*k+2] for all k, counting elements from zero.
- python中的heap是一個最小堆,也就是說,heap[0]永遠是最小的那個元素.
- 常用函數:
- heapq.heappush(heap,item) push操作
- heapq.heappop(heap) pop操作,每次返回heap[0],如果heap空了,則拋出IndexError異常
- heapq.heappushpop(heap, item) push&pop
- heapq.heapify(x) 將一個list轉化爲heap
- heapq.heapreplace(heap, item) 與pushpop不同,這個是先pop再push. 如果遇到空的heap會出現IndexError的異常.
- heapq.merge(*iterables, key=None, reverse=False) 合併,不知道有啥用…
- heapq.nlargest(n, iterable, key=None)
- heapq.nsmallest(n, iterable, key=None)
比如實現一個簡單的堆排序
1.def heapsort(iterable):
2. h=[]
3. for value in iterable:
4. heapq.heappush(h,value)
5. return[heapq.heappop(h) for i in range(len(h))]
repr和str函數
所有官方文檔都在說,str函數是給人看的,repe函數是給編譯器看的.
1. class Item:
2. def __init__(self,name):
3. self.name = name
4. def __str__(self):
5. return 'here is {0} __str__ function'.format(self.name)
6. def __repr__(self):
7. return 'here is {0} __repr__ function'.format(self.name)
8.>>> boo = Item('boo')
9.>>> boo
10.here is boo __repr__ function
11.>>> print(boo)
12.here is boo __str__ function
通過上面這段代碼簡單來看,在repr函數沒有重載的時候,默認會顯示<main.Item object at 0x03CC1D70>類似這個樣子,是一個地址. 也就是說這兩個函數都是用來顯示關於這個類的信息的. 而print函數會調用str函數,解釋器會調用repr函數.
format()函數
format函數用於做str的格式化書寫.可以使用{}來做佔位符以提供一個模板.
比如:
1.#1.使用數字佔位符
2.>>> "User ID: {0}".format("root")
3.'User ID: root'
4.#2.使用類似變量的佔位符
5.>>> 'User ID: {uid} Last seen: {last_login}'.format(uid='root',last_login = '5 Mar 2008 07:20')
6.'User ID: root Last seen: 5 Mar 2008 07:20'
7.#3.大括號的轉義
8.format("Empty dict: {{}}")
9.#4.在字典中使用
10.>>> d2={'jim':25,'tom':27}
11.>>> "jim's age is {0[jim]}".format(d2)
12."jim's age is 25"
另外關於三個控制字符,Three conversion flags are currently supported: ‘!s’ which calls str() on the value, ‘!r’ which calls repr() and ‘!a’ which calls ascii().
>>> '{0},{1!r}'.format(boo,boo)
'here is boo __str__ function,here is boo __repr__ function'
更具體的用法查文檔吧…