ヒープとBSTの違いは何ですか?
ヒープを使用する場合とBSTを使用する場合
要素を並べ替えて取得したい場合、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)
この表の平均時間はすべて、挿入を除いて最悪の時間と同じです。
*
:この回答のすべての箇所で、BST ==バランスBST。アンバランスは漸近的に吸引するため**
:この回答で説明されている簡単な変更を使用***
:log(n)
ポインターツリーヒープ、n
動的配列ヒープBSTに対するバイナリヒープの利点
O(1)
BSTの場合、バイナリヒープへの平均挿入時間はO(log(n))
です。これはヒープのキラー機能です。
フィボナッチヒープO(1)
などの償却済み(より強い)に到達する他のヒープもありますが、最悪の場合はBrodalキューですが、漸近的なパフォーマンスがないため実用的ではない可能性があります。
バイナリヒープは、動的配列またはポインターベースのツリー(BSTのみのポインターベースのツリー)の上に効率的に実装できます。したがって、ヒープの場合、サイズ変更のレイテンシを時々許容できるのであれば、よりスペース効率の高い配列の実装を選択できます。
BSTの場合、バイナリヒープの作成はO(n)
最悪の場合O(n log(n))
です。
バイナリヒープに対するBSTの利点
任意の要素の検索はO(log(n))
です。これはBSTのキラー機能です。
ヒープの場合O(n)
、最大の要素であるを除いて、それは一般的にですO(1)
。
BSTに対するヒープの「誤った」利点
ヒープはO(1)
最大値、BSTを見つけることO(log(n))
です。
これはよくある誤解です。BSTを変更して最大の要素を追跡し、その要素が変更される可能性がある場合はいつでも更新することは簡単です。大きな1つのスワップを挿入すると、2番目に大きいものを見つけます。バイナリ検索ツリーを使用してヒープ操作をシミュレートできますか?(Yeoが言及)。
実際、これはBSTと比較したヒープの制限です。最も効率的な検索は、最大の要素の検索のみです。
平均バイナリヒープ挿入は O(1)
出典:
直感的な議論:
バイナリヒープでO(1)
は、同じ理由で特定のインデックスの値を増やすこともできます。しかし、それを行う場合は、ヒープ操作で追加のインデックスを最新に保つ必要がある可能性があります。最小ヒープベースの優先度キューにO(logn)の減少キー操作を実装する方法は?ダイクストラなど。追加の時間コストなしで可能です。
実際のハードウェアでのGCC C ++標準ライブラリ挿入ベンチマーク
C ++ std::set
(Red-black tree BST)とstd::priority_queue
(dynamic array heap)の挿入をベンチマークして、挿入時間が適切かどうかを確認しました。
明らかに:
ヒープ挿入時間は基本的に一定です。
動的な配列のサイズ変更ポイントをはっきりと見ることができます。システムノイズを超えて何でも見ることができるように、 1万回の挿入ごとに平均化しているため、これらのピークは実際には表示されている値よりも約10倍大きくなっています。
ズームされたグラフは、基本的に配列のサイズ変更ポイントのみを除外し、ほとんどすべての挿入が25ナノ秒未満であることを示しています。
BSTは対数です。すべての挿入は、平均的なヒープ挿入よりもはるかに低速です。
BSTとハッシュマップの詳細な分析:C ++のstd :: map内にはどのようなデータ構造がありますか?
GCC C ++標準ライブラリのgem5でのベンチマーク挿入
gem5は完全なシステムシミュレータであるため、で無限に正確なクロックを提供しm5 dumpstats
ます。そのため、個々の挿入のタイミングを推定するためにそれを使用しようとしました。
解釈:
ヒープはまだ一定ですが、行がいくつかあり、より高い各行がより疎であることがより詳細にわかります。
これは、メモリアクセスのレイテンシに対応している必要があり、挿入回数が多くなるほど処理が遅くなります。
TODO BSTは対数的ではなく、やや一定しているように見えないため、BSTを完全に解釈することはできません。
この詳細を使用すると、いくつかの明確な線を確認することもできますが、それらが何を表しているのかわかりません。
aarch64 HPI CPUでのこのBuildrootセットアップでベンチマーク。
BSTはアレイに効率的に実装できません
ヒープ操作では、単一のツリーブランチをバブルアップまたはダウンするだけでよいので、O(log(n))
最悪の場合はスワップがO(1)
平均的です。
BSTのバランスを保つには、ツリーの回転が必要です。これにより、最上位の要素を別の要素に変更でき、配列全体を移動する必要があります(O(n)
)。
ヒープはアレイに効率的に実装できます
親と子のインデックスは、次のように現在のインデックスから計算できます。
BSTのようなバランシング操作はありません。
削除分はトップダウンである必要があるため、最も心配な操作です。ただし、ここで説明するように、ヒープの1つのブランチを「浸透」させることによって、常にそれを行うことができます。ヒープは常にバランスが取れているため、これはO(log(n))の最悪のケースにつながります。
削除するノードごとに1つのノードを挿入する場合、削除が支配するためヒープが提供する漸近的なO(1)平均挿入の利点が失われ、BSTを使用することもできます。ただし、ダイクストラは削除ごとにノードを数回更新するため、問題ありません。
動的配列ヒープとポインターツリーヒープ
ヒープはポインターヒープの上に効率的に実装できます。効率的なポインターベースのバイナリヒープ実装を作成することは可能ですか?
動的配列の実装は、よりスペース効率的です。各ヒープ要素にaへのポインタのみが含まれているとしますstruct
。
ツリーの実装は、要素ごとに3つのポインタ(親、左の子、右の子)を格納する必要があります。したがって、メモリ使用量は常に4n
(3つのツリーポインター+ 1つのstruct
ポインター)です。
Tree BSTには、さらに黒赤みなどのバランス情報も必要です。
動的配列の実装は2n
、2倍した直後のサイズにすることができます。したがって、平均的にはそうなります1.5n
。
一方、ツリーヒープはワーストケースの挿入の方が優れています。これは、バッキングダイナミック配列をコピーしてサイズを2倍にするのがO(n)
最悪の場合であり、ツリーヒープは各ノードに新しい小さな割り当てを行うだけだからです。
それでも、バッキングアレイの倍増はO(1)
償却されるため、レイテンシを最大にすることになります。ここで言及。
哲学
BSTは、親とすべての子孫の間のグローバルプロパティを維持します(左に小さい、右に大きい)。
BSTの最上位ノードは中央の要素であり、維持するためにグローバルな知識が必要です(そこに存在するより小さな要素とより大きな要素の数を知っています)。
このグローバルプロパティは維持にコストがかかります(log n insert)が、より強力な検索(log n search)を提供します。
ヒープは、親と直接の子(親>子)の間のローカルプロパティを維持します。
ヒープの最上位ノードは大きな要素であり、維持するためにローカルの知識のみが必要です(親を知っている)。
BSTとヒープとハッシュマップの比較:
BST:次のいずれかになります。
ヒープ:単なる選別機です。最小/最大の要素のみを高速でチェックできるため、効率的な順序なしセットにすることはできません。
ハッシュマップ:順序付けを混同するため、効率的なソーティングマシンではなく、順序付けされていないセットのみを使用できます。
二重リンクリスト
二重にリンクされたリストは、最初の項目が最も優先されるヒープのサブセットと見なすことができるため、ここでもそれらを比較してみましょう。
O(1)
アイテムへのポインターがあり、更新が非常に簡単であるため、最悪の場合O(1)
平均、リンクリストよりも悪い。より一般的な挿入位置を持つことのトレードオフ。O(n)
両方この使用例は、ヒープのキーが現在のタイムスタンプである場合です。その場合、新しいエントリは常にリストの先頭に移動します。そのため、正確なタイムスタンプを完全に忘れて、リスト内の位置を優先度として保持することさえできます。
これは、LRUキャッシュの実装に使用できます。同じようにダイクストラのようなヒープのアプリケーションのために、あなたはすぐに更新するノードを見つけるために、リストの対応するノードの鍵から追加HashMapを維持したいと思うでしょう。
さまざまなバランスBSTの比較
これまで見てきた「バランスBST」として一般的に分類されるすべてのデータ構造の漸近的な挿入時間と検索時間は同じですが、BBSTによってトレードオフは異なります。私はまだこれを完全に研究していませんが、これらのトレードオフをここで要約するとよいでしょう:
こちらもご覧ください
CSでの同様の質問:https : //cs.stackexchange.com/questions/27860/whats-the-difference-between-a-binary-search-tree-and-a-binary-heap
ヒープは、より高いレベルの要素がより低いレベルの要素よりも大きい(max-heapの場合)または小さい(min-heapの場合)ことを保証しますが、BSTは順序(「左」から「右」へ)を保証します。要素をソートしたい場合は、BSTを使用してください。
[1, 5, 9, 7, 15, 10, 11]
有効な最小ヒープを表しますが、7
レベル3は9
レベル2 よりも小さくなっています。視覚化については、たとえば、ヒープのサンプルWikipedia画像の25
および19
要素を参照してください。(要素は必ずしも一意ではないため、要素間の不等関係は厳密ではないことにも注意してください。)
ヒープを使用する場合とBSTを使用する場合
ヒープはfindMin / findMax(O(1)
)で優れていますが、BSTはすべての find()で優れていますO(logN)
。挿入はO(logN)
両方の構造のためです。findMin / findMax(たとえば、優先度関連)のみに関心がある場合は、ヒープを使用してください。すべてをソートしたい場合は、BSTを使用してください。
ここからの最初の数枚のスライドでは、非常に明確に説明しています。
他の人が述べたように、ヒープを行うことができfindMin
、または findMax
同一のデータ構造に双方O(1)ではありません。ただし、findMin / findMaxの方がヒープの方が優れているとは思いません。実際、わずかな変更で、BSTはO と O(1)の両方 findMin
で 実行できますfindMax
。
この変更されたBSTでは、データ構造を変更する可能性のある操作を実行するたびに、最小ノードと最大ノードを追跡します。たとえば、挿入操作では、最小値が新しく挿入された値より大きいかどうかを確認し、新しく追加されたノードに最小値を割り当てることができます。同じ手法を最大値に適用できます。したがって、このBSTにはO(1)で取得できるこれらの情報が含まれています。(バイナリヒープと同じ)
このBST(バランスBST)では、pop min
またはのpop max
場合、次に割り当てられる最小値は最小ノードの後続であり、次に割り当てられる最大値は最大ノードの先行です。したがって、O(1)で実行されます。ただし、ツリーのバランスを再調整する必要があるため、引き続きO(log n)が実行されます。(バイナリヒープと同じ)
以下のコメントであなたの考えを聞きたいです。ありがとう:)
同様の質問への相互参照バイナリ検索ツリーを使用してヒープ操作をシミュレートできますか?BSTを使用したヒープのシミュレーションに関する詳細については、
popMin
またはpopMax
O(1)ではありませんが、すべての削除操作を再調整する必要があるバランスBSTである必要があるため、O(log n)です。したがって、バイナリヒープと同じpopMin
かpopMax
、O(log n)を実行します
二分探索木は定義を使用します。つまり、すべてのノードについて、その左側のノードの値(キー)は小さく、その右側のノードの値(キー)は大きくなります。
ヒープと同様に、バイナリツリーの実装は次の定義を使用します。
AとBがノードであり、BがAの子ノードである場合、Aの値(キー)はBの値(キー)以上でなければなりません。つまり、key(A)≥key(B )。
http://wiki.answers.com/Q/Difference_between_binary_search_tree_and_heap_tree
今日も同じ質問に出題され、正解しました。笑顔... :)
ヒープ上のBSTの別の使用法。重要な違いがあるため:
ヒープを介したBSTの使用:次に、データ構造を使用してフライトの着陸時間を格納するとします。着陸時間の差が「d」未満の場合、着陸へのフライトをスケジュールできません。そして、多くのフライトがデータ構造(BSTまたはヒープ)に着陸するようにスケジュールされていると想定します。
ここで、tに着陸する別のフライトをスケジュールします。したがって、tとその後続および先行(dである必要があります)との差を計算する必要があります。 したがって、我々は速いそれをしないいる、このためにBSTが必要になりますつまりバランスがあればO(LOGN)で。
編集:
ソート ヒープはO(N LOGN)でそれを行うことができながら、BSTは、時間、ソート順(INORDERトラバーサル)の要素を印刷するためのO(N)時間を要します。ヒープは最小要素を抽出し、配列を再度ヒープ化します。これにより、O(n logn)時間でソートが行われます。
from unsorted to sorted sequence. O(n) time for inorder traversal of a BST, which gives sorted sequence.
まあ、ソートされていないシーケンスからBSTまで、O(n logn)時間未満のキー比較に基づく方法はわかりません。これは、BSTをシーケンス部分に支配します。(一方、O(n)ヒープ構造があります。)ヒープがソートされていないこととBSTがソートされていることを示すのは(無意味な場合でも)公平だと思います。
ヒープは、高レベルの要素が低レベルの要素よりも大きい(最大ヒープの場合)または小さい(最小ヒープの場合)ことを保証するだけです。
私は上記の答えが好きで、私のコメントを自分のニーズと使用法にもっと具体的に当てはめます。n個の場所のリストを取得して、各場所から特定のポイント(0,0)までの距離を検索し、距離が短いamの場所を返す必要がありました。私はヒープである優先キューを使用しました。距離を見つけてヒープを配置するために、挿入ごとにn(log(n))n-locations log(n)かかりました。次に、最短距離でmを取得するために、m(log(n))m-locations log(n)の山積みの削除が行われました。
これをBSTで行う必要がある場合、n(n)の最悪の場合の挿入が必要になります(最初の値は非常に小さく、他のすべての値は順次長くなり、ツリーは右の子のみまたは左の子にまたがるとしますどんどん小さくなった場合、最小値はO(1)時間かかったはずですが、再びバランスを取る必要がありました。したがって、私の状況と上記のすべての答えから、最小または最大の優先度ベースの値が後になって初めて、ヒープ用。