ハッシュテーブルよりもバイナリ検索ツリーの利点は何ですか?
ハッシュテーブルはTheta(1)時間で任意の要素を検索でき、要素を追加するのも同じくらい簡単です。
ハッシュテーブルよりもバイナリ検索ツリーの利点は何ですか?
ハッシュテーブルはTheta(1)時間で任意の要素を検索でき、要素を追加するのも同じくらい簡単です。
回答:
バイナリ検索ツリー(参照ベース)はメモリ効率が良いことに注意してください。必要以上にメモリを予約することはありません。
たとえば、ハッシュ関数に範囲があるR(h) = 0...100
場合、たとえ20要素をハッシュしている場合でも、100(points-to)要素の配列を割り当てる必要があります。バイナリ検索ツリーを使用して同じ情報を格納する場合は、必要なだけのスペースとリンクに関するメタデータを割り当てるだけです。
他の誰も指摘していない利点の1つは、バイナリ検索ツリーを使用すると、範囲検索を効率的に実行できることです。
私の考えを説明するために、私は極端なケースを作りたいと思います。キーが0〜5000のすべての要素を取得したいとします。実際には、そのような要素は1つだけで、キーが範囲内にない他の要素は10000だけです。BSTは、答えを得ることのできないサブツリーを検索しないため、範囲検索を非常に効率的に実行できます。
一方、ハッシュテーブルで範囲検索を実行するにはどうすればよいですか?O(n)であるすべてのバケットスペースを反復処理する必要があるか、または1,2,3,4 ... 5000までのそれぞれが存在するかどうかを調べる必要があります。(0から5000までのキーは無限セットですか?たとえば、キーは10進数にすることができます)
バイナリツリーの1つの「利点」は、すべての要素を順番にリストするためにトラバースできることです。これはハッシュテーブルでは不可能ではありませんが、通常の操作ではありません。
他のすべての良いコメントに加えて:
一般に、ハッシュテーブルは、バイナリツリーと比較して、より少ないメモリ読み取りを必要とするより良いキャッシュ動作を備えています。ハッシュテーブルの場合、通常は、データを保持している参照にアクセスする前に、1回の読み取りのみが発生します。バイナリツリーは、それがバランスのとれたバリアントである場合、定数kに対してk * lg(n)のメモリ読み取りのオーダーで何かを必要とします。
一方、敵がハッシュ関数を知っている場合、敵はハッシュテーブルを強制的に衝突させ、パフォーマンスを大幅に低下させる可能性があります。回避策は、ハッシュ関数をファミリからランダムに選択することですが、BSTにはこの欠点はありません。また、ハッシュテーブルのプレッシャーが大きくなりすぎると、ハッシュテーブルを拡大して再割り当てする傾向があり、コストのかかる操作になる可能性があります。ここではBSTの動作が単純であり、突然大量のデータを割り当ててリハッシュ操作を行う傾向はありません。
ツリーは、最終的な平均データ構造になる傾向があります。それらはリストとして機能し、並列処理のために簡単に分割でき、O(lg n)のオーダーで高速な削除、挿入、検索ができます。彼らは特に何もしませんが、過度に悪い振る舞いもしません。
最後に、BSTはハッシュテーブルと比較して(純粋な)関数型言語で実装する方がはるかに簡単であり、破壊的な更新を実装する必要はありません(上記のPascalによる永続化の引数)。
BSTs are much easier to implement in (pure) functional languages compared to hash-tables
- 本当に?今、関数型言語を学びたい!
ハッシュテーブルに対するバイナリツリーの主な利点は、ハッシュツリーでは実行できない(簡単に、すばやく)2つの操作がバイナリツリーに追加されることです。
任意のキー値に最も近い(必ずしも等しいとは限らない)要素を見つける(または、上または下に最も近い要素)
ソートされた順序でツリーのコンテンツを反復処理します
2つは接続されています。バイナリツリーはその内容をソートされた順序で保持するため、ソートされた順序を必要とするものは簡単に実行できます。
(バランスのとれた)バイナリ検索ツリーには、その漸近的な複雑さが実際には上限であるという利点もありますが、ハッシュテーブルの「一定」の時間は償却されます。不適切なハッシュ関数がある場合、最終的に線形時間に低下する可能性があります、定数ではなく。
ハッシュテーブルは、最初に作成されたときにより多くのスペースを必要とします-(まだ挿入されているかどうかにかかわらず)まだ挿入されていない要素用の利用可能なスロットがあり、バイナリ検索ツリーは必要なだけ大きくなりますあります。また、ハッシュテーブルにさらにスペースが必要な場合、別の構造への拡張には時間がかかる可能性がありますが、それは実装に依存する場合があります。
バイナリツリーは検索と挿入が低速ですが、中置走査の非常に優れた機能を備えています。つまり、ソートされた順序でツリーのノードを反復処理できるということです。
ハッシュテーブルのエントリを反復処理しても、すべてがメモリに分散しているため、あまり意味がありません。
バランスのとれた二分探索木(BST)でハッシュテーブルを実装できます。これにより、O(log n)ルックアップ時間が得られます。これの利点は、大きな配列を割り当てないため、使用するスペースが少なくなる可能性があることです。キーを順番に繰り返し処理することもできます。
BSTは、O(logn)時間で "findPredecessor"および "findSuccessor"操作(次に小さい要素と次に大きい要素を見つける)も提供します。これも非常に便利な操作です。ハッシュテーブルはその時間効率を提供できません。
ソートされた方法でデータにアクセスする場合は、ソートされたリストをハッシュテーブルと並行して維持する必要があります。良い例は、.Netの辞書です。(http://msdn.microsoft.com/en-us/library/3fcwy8h6.aspxを参照してください)。
これには、挿入が遅くなるだけでなく、Bツリーよりも多くのメモリが消費されるという副作用があります。
さらに、bツリーがソートされるため、結果の範囲を見つけたり、ユニオンやマージを実行したりするのは簡単です。
また、用途によって異なりますが、ハッシュを使用すると、完全一致を見つけることができます。範囲を照会する場合は、BSTが最適です。大量のデータe1、e2、e3 ..... enがあるとします。
ハッシュテーブルを使用すると、一定の時間内に任意の要素を見つけることができます。
e41より大きくe8より小さい範囲の値を検索する場合、BSTはそれをすばやく見つけることができます。
重要なのは、衝突を回避するために使用されるハッシュ関数です。もちろん、衝突を完全に回避することはできません。その場合は、チェーンまたはその他の方法を使用します。これにより、最悪の場合、検索が一定の時間ではなくなります。
満杯になると、ハッシュテーブルはバケットサイズを増やし、すべての要素を再度コピーする必要があります。これは、BSTにはない追加のコストです。
ハッシュテーブルはインデックス作成には適していません。範囲を検索する場合は、BSTの方が適しています。これが、ほとんどのデータベースインデックスがハッシュテーブルではなくB +ツリーを使用する理由です。
バイナリ検索ツリーは、キーにいくつかの合計順序(キーは比較可能)が定義されていて、順序情報を保持したい場合、辞書を実装するのに適しています。
BSTは注文情報を保持するため、ハッシュテーブルを使用して(効率的に)実行できない4つの追加の動的セット操作を提供します。これらの操作は次のとおりです。
すべてのBST操作と同様に、これらすべての操作はO(H)の時間の複雑さを持っています。さらに、格納されているすべてのキーはBSTでソートされたままなので、ツリーを順番にたどるだけで、ソートされたキーのシーケンスを取得できます。
要約すると、挿入、削除、削除の操作だけが必要な場合、ハッシュテーブルは(ほとんどの場合)パフォーマンスにおいて無敵です。ただし、上記の操作の一部またはすべてが必要な場合は、BST、できれば自己バランスBSTを使用する必要があります。
ハッシュマップは、セット連想配列です。したがって、入力値の配列はバケットにプールされます。オープンアドレッシングスキームでは、バケットへのポインターがあり、バケットに新しい値を追加するたびに、バケットのどこに空きスペースがあるかがわかります。これを行うにはいくつかの方法があります。バケットの最初から開始し、毎回ポインタをインクリメントして、それが占有されているかどうかをテストします。これは線形プローブと呼ばれます。次に、addのようなバイナリ検索を実行できます。ここでは、バケットの先頭と、空きスペースを検索するたびに2倍または2倍の差を2倍にします。これは二次プロービングと呼ばれます。OK。これらの両方の方法の問題は、バケットが次のバケットアドレスにオーバーフローした場合に、
OK。しかし、もしあなたがリンクリストを使用するなら、そのような問題はないはずですよね?はい、リンクリストではこの問題は発生しません。各バケットがリンクリストで始まることを考慮し、バケットに100要素がある場合、リンクリストの最後に到達するためにそれらの100要素をトラバースする必要があるため、List.add(Element E)には時間がかかります
linkedlist実装の利点は、オープンアドレス指定実装の場合のように、メモリ割り当て操作とすべてのバケットのO(N)転送/コピーが不要なことです。
したがって、O(N)操作を最小限に抑える方法は、実装をバイナリ検索ツリーの実装に変換することです。ここで、検索操作はO(log(N))であり、その値に基づいて要素をその位置に追加します。BSTの追加機能は、ソートされていることです。