我有一個調度算法,其中我比較了優先級/任務元組列表的最小值和最大值,對它們進行了一些更改其優先級的操作,然後將它們重新插入到列表中並使列表適當更新。 heapq會是最好的數據結構嗎?我如何進行初始比較(基本上是確定優先級值是否足夠分開以便需要進一步操作;如果不是該功能將停止),而不彈出?一旦比較完成,我將如何將最大值與最小值一起計算,因爲heapq僅用於突出最小值?使用分鐘和Maxes - Heapq適當?
回答
heapq
只提供最小堆 - 也就是說,您可以在O(log N)時間彈出min
值,但不會輸出max
值。
如果您想要一個類似於heapq
的雙面數據結構,有幾個基本選項。
首先,常規最小堆有什麼問題?這不僅僅是API;找到最大值需要O(n)
時間,而不是O(1)
時間,因此彈出它需要O(n)
而不是O(log n)
,這是您想要改進的關鍵。
簡單的黑客攻擊包括保留兩個堆,一個是正常值,一個是正常值,因此它們向後排序。下面是僞代碼實現:
def push(self, value):
insert into both normal and reversed heaps
def minpop(self):
check that the min value of normal hasn't reached the min value of reversed
pop and return the min value of normal
def maxpop(self):
check that the min value of reversed hasn't reached the min value of normal
pop and return the min value of reversed
乍一看,這似乎是每個操作的最壞情況下的行爲應該是正好兩倍於minheap的,但事實並非如此。特別是,最壞情況的空間是有史以來插入的元素的數量,這可能比插入的數字多兩倍 - 刪除的數字。 (例如,如果您插入了1000個項目並刪除了100,900 >> 200)。
有許多用例不適用,並且它應該很明顯,如果它不起作用用例。但是當它適合是時,它很簡單。
如果它不是合適,您可以使用真正的最小最大堆。這基本上只是將最小堆的normal
和reversed
版本交錯爲單個結構,並且可以輕鬆地在上面的「檢查」情況下做正確的事情(而不是將值留在後面)。
但是,如果您希望雙端優先級隊列具有對稱性能,那麼您實際上無法比平衡樹或跳過列表做得更好。 (好吧,不是爲了一般目的,如果你有特定的行爲特徵,那可能是不正確的。)AVL樹,紅黑樹和跳過列表的實現比min-max二進制堆多得多。因此,搜索「平衡樹」,「紅黑樹」,「AVL樹」,「跳過列表」等的PyPI和ActiveState配方,你會發現像bintrees
和skiplist
這些都應該工作。我想推薦blist
。它使用一棵平衡樹和一個數組的特殊混合,而不是一個經過深入研究的數據結構,乍一看可能會讓你認爲它不太值得信賴。但是,我相信它比任何競爭模塊都有更多的使用和實際測試,並且它也經過了相當多的優化。 (在處理A * log Bn + C
性能時,更改A
或C
通常比更改B
更具影響力。)它也有一個很好的界面 - 實際上只有少數界面。如果您使用blist.sortedlist
,則可以執行sl[0]
,sl[-1]
,sl.pop(0)
,sl.pop(-1)
和sl.add(x)
,幾乎完全如您所料。
因此,您的代碼會是這個樣子(如果我沒有理解你的英文說明):
class MyQueue(object):
def __init__(self):
self.sl = blist.sortedlist(key=operator.itemgetter(0))
def add(self, priority, task):
self.sl.add((priority, task))
def step(self):
if self.sl[-1][0] - self.sl[0][0] < MyQueue.EPSILON:
return
minprio, mintask = self.sl.pop(0)
maxprio, maxtask = self.sl.pop(-1)
newminprio, newmaxprio = recalc_priorities(minprio, maxprio)
self.add(newminprio, mintask)
self.add(newmaxprio, maxtask)
的問題與任何這些方法是,偷看雙方最壞的情況是O(log N)
而非O(1)
。但有解決這個簡單的方法,如果這是唯一的操作,您需要:只要保持這些值緩存:
class MyQueue(object):
def __init__(self):
self.sl = blist.sortedlist(key=operator.itemgetter(0))
self.minprio, self.maxprio = None, None
def add(self, priority, task):
self.sl.add((priority, task))
if prio < self.minprio: self.minprio = prio
elif prio > self.maxprio: self.maxprio = prio
def step(self):
if self.maxprio - self.minprio < MyQueue.EPSILON:
return
minprio, mintask = self.sl.pop(0)
maxprio, maxtask = self.sl.pop(-1)
newminprio, newmaxprio = recalc_priorities(minprio, maxprio)
self.add(newminprio, mintask)
self.add(newmaxprio, maxtask)
self.minprio, self.maxprio = sl[0][0], sl[-1][0]
這使得通過step
O(1)
代替O(log n)
快速路徑,並留下所有現有O(log n)
操作仍O(log n)
。
另請參閱Wikipedia,其中討論了可以替代二進制堆的其他類堆。最後
一個說明,這igorrs的評論提醒我:
有很多種不同的數據結構,將在這裏得到你同樣的最壞情況下的算法複雜度。有時,任何避免O(n)
都足夠好,所以你應該用最簡單的實現去完成它。但有時(特別是對於許多操作,但小數據或非典型數據),常數因子,最佳情況等可以產生巨大的差異。在這種情況下,正確的做法是構建多個實現並測試真實數據,並查看最快的數據。
假設你正在考慮一個堆,我可以假設你的預期(與n
爲元素的總數)爲:
- 找到最小的密鑰和
O(1)
時間最大的關鍵。 - 在
O(log(n))
時間內,用最小的鍵和具有最大鍵的元素重新插入(使用更改的鍵)。
這可以用min-max heap完成。不幸的是,我不認爲這在Python的標準庫中可用。
如果你放鬆了第一個要求,任何平衡樹(例如紅黑色)都可以實現,所有期望的操作都需要O(log(n))
時間。
Python的標準庫也不提供任何平衡樹,所以你將不得不推出自己的或尋找一個實現。
min-max堆不能像兩個方向上的最小堆一樣有效,只是一個或另一個,這意味着它的效果與平衡樹一樣好(除非您經常要檢查min值而不會彈出),所以你原來的答案已經足夠好了。 – abarnert
使用min-max堆,可以在'O(1)'中找到最小和最大的鍵。這很重要,因爲@ user1427661 **不想每次都彈出**(這個問題是關於比較兩個鍵並在某些情況下什麼都不做的)。 – igorrs
是的,但是,比如說,紅黑樹和頂部和底部的緩存保留了相同的性能保證。這裏的關鍵是他希望偷看雙方,如果他不立即流行,除了插入新的價值外,不會改變任何事情。所以,你可以在彈出和插入時更新緩存,並給你'O(1)'偷看,而不會影響性能。 (除非我不理解他的整個用例。)你從一個緩存樹中得不到的最重要的東西是'heappushpop',但是你也不會在min-最大堆。 – abarnert
- 1. 使用分鐘(當天)
- 2. MYSQL綁maxes
- 3. 與heapq
- 4. 如何在O(lgn)中heapq heapq?
- 5. 最大和最小; heapq python
- 6. 使用PHP將小時和分鐘轉換爲分鐘
- 7. 如何使用JSpinner分別更改小時,分鐘和秒鐘?
- 8. 適當使用DL和DD?
- 9. 使用chrono :: time_point獲取當前的小時數和分鐘數
- 10. 不使用分鐘
- 11. 使用分鐘(鍵)
- 12. 小時和分鐘
- 13. 如何將分鐘轉換爲PHP和負數分鐘的小時和分鐘?
- 14. 小時和分鐘用JavaScript
- 15. PostgreSQL查詢當前分鐘
- 16. 使用AmCharts在幾分鐘和幾秒鐘內設置StockEvents
- 17. 使用分鐘和秒鐘的簡單Jquery計時器
- 18. CakePHP 2.2.3當使用分鐘間隔和默認分鐘值時,FormHelper不能設置默認子午線
- 19. 分鐘超過60分鐘的小時數和分鐘數總和
- 20. 適當的SVN使用分支和中繼
- 21. 瞭解heapq push pop
- 22. 在T-SQL中將分鐘轉換爲小時和分鐘
- 23. 限制使用5分鐘
- 24. 適當使用shared_ptr?
- 25. 適當使用GLKBaseEffect
- 26. 適當使用CCTextureCache
- 27. 適當使用UINavigationController?
- 28. 適當使用count_all_results()?
- 29. 訪問heapq的索引和長度?
- 30. 在python中繼承'heapq'和'deque'?
+1用於發佈在給定約束條件下解決問題的實際代碼。 – igorrs
我遲到了,但我只想指出heapq可以通過簡單地否定優先級而轉換成最大堆。 – st0le
@ st0le:如果你閱讀的第一行之外,這個答案已經很明顯了。 OP的問題是他想要一個雙面堆,而最困難的部分是配對minheap和maxheap來獲得這個,而不僅僅是構建一個maxheap。 – abarnert