簡體   English   中英

堆與二叉搜索樹(BST)

[英]Heap vs Binary Search Tree (BST)

堆和 BST 有什么區別?

何時使用堆,何時使用 BST?

如果您想以排序方式獲取元素,BST 是否優於堆?

概括

          Type      BST (*)   Heap
Insert    average   log(n)    1
Insert    worst     log(n)    log(n) or n (***)
Find any  worst     log(n)    n
Find max  worst     1 (**)    1
Create    worst     n log(n)  n
Delete    worst     log(n)    log(n)

此表上的所有平均時間都與其最差時間相同,但 Insert 除外。

  • * :在這個答案的任何地方,BST == Balanced BST,因為不平衡漸進地吸
  • ** :使用此答案中解釋的簡單修改
  • *** : log(n)為指針樹堆, n為動態數組堆

二叉堆相對於 BST 的優勢

BST 相對於二叉堆的優勢

  • 搜索任意元素是O(log(n)) 是 BST 的殺手鐧。

    對於堆,它通常是O(n) ,除了最大的元素是O(1)

堆相對於 BST 的“假”優勢

  • 堆是O(1)來找到最大值,BST O(log(n))

    這是一個常見的誤解,因為修改 BST 以跟蹤最大元素並在該元素可以更改時更新它是微不足道的:插入較大的一個交換時,刪除時找到第二大的。 我們可以使用二叉搜索樹來模擬堆操作嗎? 由 Yeo提到)。

    實際上,與 BST 相比,這是堆的一個限制唯一有效的搜索是對最大元素的搜索。

平均二進制堆插入是O(1)

資料來源:

直觀的論證:

  • 底層樹級別的元素比頂層多,所以新元素幾乎肯定是 go 在底部
  • 堆插入從底部開始,BST必須從頂部開始

在二叉堆中,增加給定索引處的值也是O(1)出於同樣的原因。 但是如果你想這樣做,你可能希望在堆操作上保持一個額外的索引是最新的如何為基於最小堆的優先級隊列實現 O(logn) 減少鍵操作? 例如對於 Dijkstra。 無需額外時間成本即可實現。

GCC C++ 標准庫在真實硬件上插入基准

我對 C++ std::set紅黑樹 BST )和std::priority_queue動態數組堆)插入進行了基准測試,看看我對插入時間是否正確,這就是我得到的:

在此處輸入圖像描述

  • 基准代碼
  • plot 腳本
  • plot 數據
  • tested on Ubuntu 19.04, GCC 8.3.0 in a Lenovo ThinkPad P51 laptop with CPU: Intel Core i7-7820HQ CPU (4 cores / 8 threads, 2.90 GHz base, 8 MB cache), RAM: 2x Samsung M471A2K43BB1-CRC (2x 16GiB , 2400 Mbps), SSD: 三星 MZVLB512HAJQ-000L7 (512GB, 3,000 MB/s)

這么清楚:

  • 堆插入時間基本上是恆定的。

    我們可以清楚地看到動態數組調整大小點。 由於我們平均每 10k 插入能夠看到任何高於系統噪聲的東西,這些峰值實際上比顯示的大約 10k 倍!

    縮放圖基本上只排除了數組調整大小點,並顯示幾乎所有插入都低於 25 納秒。

  • BST 是對數的。 所有插入都比平均堆插入慢得多。

  • BST vs hashmap詳細分析在: What data structure is inside std::map in C++?

gem5 上的 GCC C++ 標准庫插入基准

gem5是一個完整的系統模擬器,因此可以通過m5 dumpstats提供無限准確的時鍾。 所以我試圖用它來估計單個插入的時間。

在此處輸入圖像描述

解釋:

  • heap 仍然是常量,但現在我們更詳細地看到有幾行,並且每更高的行都更稀疏。

    這必須對應於 memory 訪問延遲是針對越來越高的插入完成的。

  • TODO 我無法真正完全解釋 BST,因為它看起來不是那么對數,而且更恆定。

    然而,有了這個更詳細的信息,我們還可以看到一些不同的線條,但我不確定它們代表什么:我希望底線更薄,因為我們插入了頂部底部?

在 aarch64 HPI CPU上使用此 Buildroot 設置進行基准測試。

BST 無法在陣列上有效實現

堆操作只需要向上或向下冒泡單個樹分支,因此O(log(n))最壞情況交換, O(1)平均。

保持 BST 平衡需要樹旋轉,這可以將頂部元素更改為另一個,並且需要移動整個數組( O(n) )。

堆可以有效地在數組上實現

可以從當前索引計算父索引和子索引,如下所示

沒有像 BST 這樣的平衡操作。

刪除 min 是最令人擔憂的操作,因為它必須自上而下。 但它總是可以通過“滲透”堆的單個分支來完成,如此處所述 這會導致 O(log(n)) 最壞的情況,因為堆總是很平衡。

如果您為刪除的每個節點插入一個節點,那么您將失去堆提供的漸近 O(1) 平均插入的優勢,因為刪除將占主導地位,您不妨使用 BST。 然而,Dijkstra 會為每次刪除更新節點多次,所以我們很好。

動態數組堆與指針樹堆

堆可以在指針堆之上有效地實現: 是否有可能做出有效的基於指針的二進制堆實現?

動態數組實現更節省空間。 假設每個堆元素只包含一個指向struct的指針:

  • 樹實現必須為每個元素存儲三個指針:父、左子和右子。 因此 memory 的使用量始終為4n (3 個樹指針 + 1 個struct指針)。

    樹 BST 還需要進一步的平衡信息,例如黑紅度。

  • 動態數組實現可以在加倍后大小2n 所以平均而言它將是1.5n

另一方面,樹堆有更好的最壞情況插入,因為將支持的動態數組復制到其大小的兩倍需要O(n)最壞情況,而樹堆只是為每個節點進行新的小分配。

盡管如此,后備數組加倍是O(1)攤銷的,所以它歸結為最大延遲考慮。 這里提到了

哲學

  • BST 在父節點和所有子節點(左更小,右更大)之間維護一個全局屬性。

    BST 的頂部節點是中間元素,需要全局知識來維護(知道有多少更小和更大的元素)。

    這個全局屬性的維護成本更高(log n insert),但提供了更強大的搜索(log n search)。

  • 堆在父級和直接子級(父級 > 子級)之間維護一個本地屬性。

    堆的頂部節點是大元素,只需要本地知識來維護(知道你的父節點)。

比較 BST、堆和 Hashmap:

  • BST:可以是合理的:

    • 無序集(確定元素是否先前插入的結構)。 但是 hashmap 由於 O(1) 攤銷插入而往往更好。
    • 分揀機。 但是堆通常在這方面做得更好,這就是為什么排序比樹排序更廣為人知的原因
  • heap:只是一個分揀機。 不能成為有效的無序集,因為您只能快速檢查最小/最大元素。

  • hash map:只能是無序集,不能是高效的排序機,因為散列會混淆任何排序。

雙向鏈表

雙向鏈表可以看作是堆的子集,其中第一項具有最高優先級,所以讓我們在這里也比較一下:

  • 插入:
    • position:
      • 雙向鏈表:插入的項目必須是第一個或最后一個,因為我們只有指向這些元素的指針(除非我們有一個指向感興趣的 position 的指針,例如在迭代期間)
      • 二進制堆:插入的項目可以在任何 position 中結束。 比鏈表限制更少。
    • 時間:
      • 雙向鏈表: O(1)最壞情況,因為我們有指向項目的指針,並且更新非常簡單
      • 二叉堆:平均O(1) ,因此比鏈表差。 具有更通用插入 position 的權衡。
  • 搜索:兩者都為O(n)

一個用例是堆的鍵是當前時間戳:在這種情況下,新條目將始終 go 到列表的開頭。 所以我們甚至可以完全忘記確切的時間戳,只保留列表中的 position 作為優先級。

這可用於實現LRU 緩存 就像對於像 Dijkstra 這樣的堆應用程序,您需要從 key 到列表的相應節點保留一個額外的 hashmap,以找到要快速更新的節點。

不同平衡BST的比較

盡管到目前為止我所看到的通常歸類為“平衡 BST”的所有數據結構的漸近插入和查找時間都是相同的,但不同的 BBST 確實有不同的權衡。 我還沒有完全研究過這個,但是在這里總結一下這些權衡會很好:

  • 紅黑樹 似乎是截至 2019 年最常用的 BBST,例如它是 GCC 8.3.0 C++ 實現使用的一個
  • AVL 樹 似乎比 BST 更平衡一些,因此它可能會更好地減少查找延遲,但代價是查找成本略高。 Wiki 總結道:“AVL 樹通常與紅黑樹進行比較,因為它們都支持相同的操作集並且在基本操作上花費 [相同的] 時間。對於查找密集型應用程序,AVL 樹比紅黑樹更快,因為它們更嚴格地平衡。與紅黑樹類似,AVL 樹是高度平衡的。通常,對於任何 mu < 1/2,兩者都不是權重平衡或 mu 平衡的;也就是說,兄弟節點可以有巨大的不同數量的后代。”
  • 聲波 原始論文提到了該版本在重新平衡和旋轉操作方面的優勢。

也可以看看

CS上的類似問題: https://cs.stackexchange.com/questions/27860/whats-the-difference-between-a-binary-search-tree-and-a-binary-heap

堆只是保證較高級別的元素比較低級別的元素更大(對於最大堆)或更小(對於最小堆),而 BST 保證順序(從“左”到“右”)。 如果你想要排序的元素,go 和 BST。

何時使用堆以及何時使用 BST

堆更擅長 findMin/findMax ( O(1) ),而 BST 擅長所有查找 ( O(logN) )。 兩種結構的插入都是O(logN) 如果您只關心 findMin/findMax(例如優先級相關),go 與堆。 如果您想要對所有內容進行排序,請使用帶有 BST 的 go。

這里的前幾張幻燈片非常清楚地解釋了事情。

正如其他人所提到的,堆可以在 O(1) 中執行findMinfindMax ,但不能同時在同一個數據結構中。 但是我不同意堆在 findMin/findMax 方面更好。 事實上,稍加修改,BST 就可以在 O(1) 中同時findMinfindMax

在這個修改后的 BST 中,每次執行可能會修改數據結構的操作時,您都會跟蹤最小節點和最大節點。 例如在插入操作中,您可以檢查最小值是否大於新插入的值,然后將最小值分配給新添加的節點。 可以對最大值應用相同的技術。 因此,此 BST 包含這些信息,您可以在 O(1) 中檢索它們。 (與二叉堆相同)

在這個 BST(Balanced BST)中,當你pop minpop max時,下一個要分配的 min 值是 min 節點的后繼節點,而要分配的下一個 max 值是 max 節點的前任節點。 因此它在 O(1) 中執行。 但是我們需要重新平衡樹,因此它仍然會運行 O(log n)。 (與二叉堆相同)

我很想在下面的評論中聽到你的想法。 謝謝:)

更新

交叉引用類似問題我們可以使用二叉搜索樹來模擬堆操作嗎? 有關使用 BST 模擬堆的更多討論。

二叉搜索樹使用定義:對於每個節點,它左側的節點具有較小的值(鍵),而其右側的節點具有較大的值(鍵)。

作為堆,作為二叉樹的實現使用以下定義:

如果A和B是節點,其中B是A的子節點,那么A的value(key)必須大於等於B的value(key),即key(A)≥key(B )。

http://wiki.answers.com/Q/Difference_between_binary_search_tree_and_heap_tree

我今天為我的考試跑了同樣的問題,我做對了。 微笑... :)

BST over Heap 的另一種用法; 因為一個重要的區別:

  • 在 BST 中尋找后繼者和前驅者需要 O(h) 時間。 (平衡 BST 中的 O(logn))
  • 在堆中,需要 O(n) 時間來找到某個元素的后繼或前驅。

在堆上使用 BST :現在,假設我們使用數據結構來存儲航班的着陸時間。 如果着陸時間的差異小於“d”,我們無法安排航班着陸。 並假設已安排許多航班降落在數據結構(BST 或堆)中。

現在,我們要安排另一個在t降落的航班。 因此,我們需要計算t與其后繼者和前任者的差異(應該 >d)。 因此,我們將需要一個 BST,它可以快速完成,如果平衡,則在 O(logn) 中。

編輯:

對 BST 進行排序需要 O(n) 時間來以排序順序(中序遍歷)打印元素,而 Heap 可以在 O(n logn) 時間內完成。 堆提取最小元素並重新堆集數組,使其在 O(n logn) 時間內進行排序。

將數組中的所有 n 個元素插入到 BST 需要 O(n logn)。 數組中的 n 個元素可以在 O(n) 時間內插入到堆中。 這給了堆一個明確的優勢

堆只是保證較高級別的元素比較低級別的元素更大(對於最大堆)或更小(對於最小堆)

我喜歡上面的答案,並將我的評論更具體地放在我的需要和使用上。 我必須讓 n 個位置列表找到從每個位置到特定點的距離,比如(0,0),然后返回距離更小的 am 位置。 我使用了優先隊列,它是堆。 為了找到距離並放入堆中,我每次插入都需要 n(log(n)) n-locations log(n)。 然后,為了獲得最短距離的 m,它需要 m(log(n)) m-locations log(n) 刪除堆積。

如果必須使用 BST 執行此操作,我將花費 n(n) 次最壞情況插入。(假設第一個值非常小,所有其他值依次變得越來越長,並且樹只跨越右孩子或左孩子在越來越小的情況下. 最小值會花費 O(1) 時間但我不得不再次平衡. 所以從我的情況和以上所有答案我得到的是當你只在最小或最大優先級基礎 go 的值之后為堆。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM