バイナリ検索ツリーとバイナリヒープの違いは何ですか?


回答:


63

ヒープは、高レベルの要素が低レベルの要素よりも大きい(最大ヒープの場合)または小さい(最小ヒープの場合)ことを保証するだけですが、BSTは順序(「左」から「右」)を保証します。ソートされた要素が必要な場合は、BSTを使用します。ダンテはオタクではありません

ヒープはfindMin / findMax(O(1))で優れていますが、BSTはすべての検索(O(logN))で優れています。挿入は両方の構造に対してO(logN)です。findMin / findMax(優先順位関連など)のみに関心がある場合は、ヒープを使用します。すべてを並べ替えるには、BSTを使用します。

xysunによる


findMinとfindMaxでBSTの方が優れていると思いますstackoverflow.com/a/27074221/764592
Yeo 14年

10
これはよくある誤解だと思います。バイナリツリーは、Yeoが指す最小値と最大値を見つけるために簡単に変更できます。これは実際にはヒープの制限です。効率的な検索はminまたはmax のみです。私が説明するように、ヒープの真の利点はO(1)平均挿入です:stackoverflow.com/a/29548834/895245
Ciro Santilli新疆改造中心法轮功六四事件

このビデオによると、大きい方が低いレベルの子孫でない限り、低いレベルで大きい値を持つことができます。
whoan

ヒープはルートからリーフにソートされ、BSTは左から右にソートされます。
ディープジョシ

34

バイナリ検索ツリーバイナリヒープはどちらもツリーベースのデータ構造です。

ヒープでは、ノードが子よりも優先される必要があります。最大ヒープでは、各ノードの子はそれよりも小さくなければなりません。これは、最小ヒープの反対です。

バイナリ最大ヒープ

バイナリ検索ツリー(BST)は、兄弟ノード間の特定の順序(事前順序、順序、順序)に従います。ヒープとは異なり、ツリーソートする必要あります。

バイナリ検索ツリー

O(logn)
O(1)O(logn)


1
O(logn)

32

概要

          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)

この表のすべての平均時間は、挿入を除いて最悪の時間と同じです。

  • *:この答えのどこでも、BST == Balanced BST、unbalancedは漸近的に吸い込むため
  • **:この回答で説明されている些細な変更を使用する
  • ***log(n)ポインタツリーヒープn用、動的配列ヒープ用

BSTに対するバイナリヒープの利点

バイナリヒープに対するBSTの利点

  • 任意の要素の検索はO(log(n))です。これはBSTのキラー機能です。

    ヒープの場合O(n)、最も大きい要素であるを除き、一般的にはO(1)です。

BSTに対するヒープの「偽」の利点

平均バイナリヒープ挿入は O(1)

ソース:

直感的な議論:

  • 最下位ツリーレベルには最上位レベルよりも指数関数的に多くの要素があるため、新しい要素はほとんど確実に最下位に移動します
  • ヒープの挿入は下から始まり、BSTは上から始まる必要があります

バイナリヒープでは、特定のインデックスで値を増やすこともO(1)同じ理由です。しかし、それをしたい場合は、ヒープ操作に関する追加のインデックスを最新に保ちたいと思う可能性が高いですhttps://stackoverflow.com/questions/17009056/how-to-implement-ologn-decrease-たとえば、ダイクストラの場合、最小ヒープベースの優先度キューのキー操作。追加費用なしで可能です。

実際のハードウェアでのGCC C ++標準ライブラリ挿入ベンチマーク

C ++ std::set赤黒木BST)およびstd::priority_queue動的配列ヒープ)挿入のベンチマークを行い、挿入時間について正しいかどうかを確認しました。

ここに画像の説明を入力してください

  • ベンチマークコード
  • プロットスクリプト
  • プロットデータ
  • Ubuntu 19.04、GCC 8.3.0、CPUを搭載したLenovo ThinkPad P51ラップトップでテスト済み:Intel Core i7-7820HQ CPU(4コア/ 8スレッド、2.90 GHzベース、8 MBキャッシュ)、RAM:Samsung M471A2K43BB1-CRC(2x 16GiB) 、2400 Mbps)、SSD:Samsung MZVLB512HAJQ-000L7(512GB、3,000 MB / s)

だから明らかに:

gem5のGCC C ++標準ライブラリ挿入ベンチマーク

gem5は完全なシステムシミュレータであるため、で無限に正確なクロックを提供しm5 dumpstatsます。そこで、それを使用して個々の挿入のタイミングを推定しようとしました。

ここに画像の説明を入力してください

解釈:

  • ヒープはまだ一定ですが、ここで詳細に見ると、いくつかの行があり、各上位行はより疎です。

    これは、メモリアクセスのレイテンシに対応する必要があります。

  • TODO BSTを完全に解釈することはできません。BSTは対数的に見えず、多少一定しているためです。

    しかし、この詳細を見ると、いくつかの明確な行も見ることができますが、それらが何を表しているのかわかりません。

aarch64 HPI CPU上のこのBuildrootセットアップでベンチマークされています

BSTはアレイに効率的に実装できません

ヒープ操作は、単一のツリーブランチをバブルアップまたはダウンするだけでよいため、O(log(n))最悪の場合のスワップはO(1)平均的です。

BSTのバランスを保つには、ツリーの回転が必要です。これにより、最上部の要素を別の要素に変更できますO(n)。また、配列全体を移動する必要があります()。

ヒープをアレイに効率的に実装できます

ここに示すように、現在のインデックスから親インデックスと子インデックスを計算できます

BSTのようなバランシング操作はありません。

Delete minはトップダウンである必要があるため、最も心配な操作です。ただし、ここで説明するように、ヒープの1つのブランチを「パーコレート」することで常に実行できます。ヒープは常にバランスがとれているため、これはO(log(n))最悪のケースにつながります。

削除するノードごとに1つのノードを挿入する場合、削除が支配するため、ヒープが提供する漸近的なO(1)平均挿入の利点を失い、BSTを使用することもできます。ただし、Dijkstraは削除するたびにノードを数回更新するため、問題ありません。

動的配列ヒープとポインターツリーヒープ

ポインターヒープの上にヒープを効率的に実装できます:https : //stackoverflow.com/questions/19720438/is-it-possible-to-make-efficient-pointer-based-binary-heap-implementations

動的配列の実装は、スペース効率がより高くなります。各ヒープ要素にaへのポインタのみが含まれているとしますstruct

  • ツリーの実装は、各要素に対して、親、左の子、右の子の3つのポインターを格納する必要があります。したがって、メモリ使用量は常に4n(3ツリーポインター+ 1 structポインター)です。

    ツリーBSTには、さらにバランスの取れた情報(黒赤など)も必要です。

  • 動的配列の実装は2n、倍増直後のサイズにすることができます。ですから、平均してそうなるでしょう1.5n

一方、バッキングダイナミックアレイをコピーしてサイズを2倍にするのはO(n)最悪のケースですが、ツリーヒープはノードごとに新しい小さな割り当てを行うため、ツリーヒープにはワーストケースの挿入が適しています。

それでも、バッキングアレイの倍増はO(1)償却されるため、最大遅延の考慮事項になります。ここで言及しました

哲学

  • BSTは、親とすべての子孫の間でグローバルプロパティを維持します(左に小さく、右に大きく)。

    BSTの最上位ノードは中央の要素であり、これを維持するにはグローバルな知識が必要です(小さい要素と大きい要素がいくつあるかを知る)。

    このグローバルプロパティは、メンテナンス(log n insert)がより高価ですが、より強力な検索(log n search)を提供します。

  • ヒープは、親と直接の子(親>子)の間のローカルプロパティを維持します。

    ヒープの一番上にあるのは大きな要素です。これは、ローカルの知識(親を知ること)のみを必要とします。

二重リンクリスト

二重にリンクされたリストは、最初の項目が最も優先されるヒープのサブセットと見なすことができるので、ここでも比較してみましょう。

  • 挿入:
    • ポジション:
      • 二重リンクリスト:挿入されたアイテムは、それらの要素へのポインタのみを持っているため、最初または最後でなければなりません。
      • バイナリヒープ:挿入されたアイテムは任意の位置に配置できます。リンクリストよりも制限が少ない。
    • 時間:
      • 二重リンクリスト:O(1)アイテムへのポインタがあるため、最悪の場合、更新は本当に簡単です
      • バイナリヒープ:O(1)平均、したがってリンクリストよりも悪い。より一般的な挿入位置を持つためのトレードオフ。
  • 検索:O(n)両方の

この使用例は、ヒープのキーが現在のタイムスタンプである場合です。その場合、新しいエントリは常にリストの先頭に移動します。したがって、正確なタイムスタンプを完全に忘れて、リスト内の位置を優先順位として保持することさえできます。

これを使用して、LRUキャッシュを実装できます。同じようにダイクストラのようなヒープのアプリケーションのための、あなたはすぐに更新するノードを見つけるために、リストの対応するノードの鍵から追加HashMapを維持したいと思うでしょう。

異なるバランスBSTの比較

これまで見てきた「バランスの取れたBST」として一般的に分類されるすべてのデータ構造の漸近挿入と検索時間は同じですが、BBSTによってトレードオフが異なります。私はまだこれを十分に研究していませんが、これらのトレードオフをここにまとめるのは良いことです:

  • 赤黒の木。2019年の時点で最も一般的に使用されているBBSTのようです。たとえば、GCC 8.3.0 C ++実装で使用されているものです。
  • AVLツリー。BSTよりも少しバランスが取れているように見えるので、検索のレイテンシは、検索のコストがわずかに高くなりますが、改善される可能性があります。Wikiの要約:「AVLツリーは多くの場合、赤黒ツリーと比較されます。どちらも同じ操作セットをサポートし、基本的な操作に[同じ]時間を要するからです。赤黒木と同様に、AVL木は高さのバランスが取れています。一般に、mu <1/2の場合、どちらも重量バランスもmuバランスもありません。子孫の数が異なります。」
  • WAVL原稿用紙は、リバランスと回転操作の境界面でそのバージョンの利点を言及しています。

こちらもご覧ください

CSに関する同様の質問:バイナリ検索ツリーとバイナリヒープの違いは何ですか?


1
素晴らしい答え。ヒープの一般的な用途は、中央値、k分、上位k要素です。これらの最も一般的な操作では、minを削除してから挿入します(通常、純粋な挿入操作の少ない小さなヒープがあります)。したがって、実際には、これらのアルゴリズムではBSTを上回ることはありません。
由良

1
例外的な答え!!! 基礎となるヒープ構造としてdequeを使用することにより、サイズ変更時間を大幅に短縮できますが、チャンクへの(より小さな)ポインターの配列を再割り当てする必要があるため、O(n)最悪のケースのままです。
ブラット

13

データ構造では、懸念のレベルを区別する必要があります。

  1. この質問の抽象データ構造(保存されているオブジェクト、それらの操作)は異なります。1つは優先度キューを実装し、もう1つはセットを実装します。優先度キューは、任意の要素の検索には関心がなく、最大の優先度を持つ要素のみを検索します。

  2. 構造の具体的な実装。ここでは、どちらも(バイナリ)ツリーですが、構造的な特性は異なります。キーの相対的な順序と可能なグローバル構造の両方が異なります。(多少不正確、BSTキーでは左から右に、ヒープではトップダウンで順序付けられます。)IPlantが正しく述べているように、ヒープも「完全」でなければなりません。

  3. 低レベルの実装には最終的な違いがあります。(不均衡な)バイナリ検索ツリーには、ポインターを使用した標準実装があります。反対に、バイナリヒープには、配列を使用した効率的な実装があります(正確に構造が制限されているため)。


1

前の回答に加えて、ヒープにはヒープ構造プロパティが必要です。ツリーは満杯でなければならず、常に満杯であるとは限らない最下部のレイヤーは、左端から右端まで隙間なく埋められなければなりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.