web-dev-qa-db-ja.com

ハッシュテーブルとバランスの取れたバイナリツリー

セットまたは連想配列を実装するためにハッシュテーブルまたはバランスの取れたバイナリツリーを選択する必要がある場合、どの要素を考慮する必要がありますか?

48
peoro

一般的に、この質問には答えられません。

問題は、多くの種類のハッシュテーブルとバランスの取れたバイナリツリーがあり、それらのパフォーマンスが大きく異なることです。

したがって、素朴な答えは、必要な機能に依存します。順序付けが不要な場合はハッシュテーブルを使用し、そうでない場合はバランスの取れたバイナリツリーを使用します。

より詳細な答えを得るために、いくつかの代替案を検討しましょう。

ハッシュテーブル (いくつかの基本については、Wikipediaのエントリを参照してください)

  • すべてのハッシュテーブルがリンクリストをバケットとして使用するわけではありません。人気のある代替手段は、バイナリツリーや別のハッシュテーブル(別のハッシュ関数)などの「より良い」バケットを使用することです...
  • 一部のハッシュテーブルはバケットをまったく使用しません:オープンアドレス指定を参照してください(明らかに他の問題があります)
  • 「世界の停止と再ハッシュ」の落とし穴を回避するリニア再ハッシュと呼ばれるものがあります(これは実装の詳細の品質です)。基本的に、移行フェーズでは「新しい」テーブルにのみ挿入し、「古い」エントリを「新しい」テーブルに移動します。もちろん、移行フェーズは二重検索などを意味します...

二分木

  • 再バランスにはコストがかかります。スキップリスト(マルチスレッドアクセスにも適しています)またはスプレイツリーを検討してください。
  • 優れたアロケーターは、ポインタールックアップの問題を軽減しませんが、メモリ内のノードを一緒に「パック」できます(キャッシュ動作が向上します)。
  • Bツリーとバリアントも「パッキング」を提供します

O(1)は漸近的な複雑さであることを忘れないでください。要素が少ない場合、係数は通常(パフォーマンス面で)より重要です。これはハッシュ関数が遅い場合に特に当てはまります。 。

最後に、セットの場合、 Bloom Filters のような確率論的なデータ構造を考慮することもできます。

52
Matthieu M.

ハッシュテーブルは、通常、データを何らかの順序で保持する必要がない場合に適しています。データを並べ替えておく必要がある場合は、バイナリツリーの方が適しています。

41
supercat

近代的なアーキテクチャの価値ある点:ハッシュテーブルは、通常、負荷係数が低い場合、バイナリツリーよりも少ないメモリ読み取りを持ちます。メモリアクセスは、CPUサイクルの書き込みに比べてコストが高くなる傾向があるため、ハッシュテーブルは多くの場合より高速です。

次のバイナリツリーは、赤黒木、AVL木、またはトレジャーのように、自己バランスであると想定されています

一方、ハッシュテーブルを拡張するときにハッシュテーブルのすべてを再ハッシュする必要がある場合、これは発生する(償却される)コストのかかる操作になる可能性があります。バイナリツリーにはこの制限はありません。

バイナリツリーは、純粋に機能的な言語で実装する方が簡単です。

バイナリツリーには、自然な並べ替え順序と、すべての要素に対してツリーを歩く自然な方法があります。

ハッシュテーブルの負荷係数が低い場合、多くのメモリ領域を無駄にしている可能性がありますが、2つのポインタを使用すると、バイナリツリーはより多くの領域を占有する傾向があります。

ハッシュテーブルは、ほぼO(1)(負荷係数の処理方法によって異なります)対ビンツリーO(lg n)です。

木は「平均的な実行者」になる傾向があります。彼らが特にうまくやることは何もありませんが、彼らは特に悪いことは何もしません。

11

バイナリ検索ツリーでは、キー間の完全な順序関係が必要です。ハッシュテーブルに必要なのは、一貫性のあるハッシュ関数との等価関係または同一関係のみです。

完全な順序関係が利用可能な場合、ソートされた配列は、バイナリツリーに匹敵するルックアップパフォーマンス、ハッシュテーブルの順序での最悪の場合の挿入パフォーマンス、および両方よりも複雑さとメモリ使用量が少なくなります。

最悪の場合のルックアップの複雑度をO(K)またはO(log K)要素をソートできる場合。

ツリーとハッシュテーブルの両方の不変式は、キーが変更された場合の復元にコストがかかりますが、ソートされた配列の場合はO(n log N)未満です。

これらは、使用する実装を決定する際に考慮すべき要素です。

  1. 完全な注文関係の可用性。
  2. 同値関係に対する適切なハッシュ関数の可用性。
  3. 要素数の事前知識。
  4. 挿入、削除、検索の速度に関する知識。
  5. 比較およびハッシュ関数の相対的な複雑さ。
7
Apalala

ハッシュテーブルは検索が高速です。

  • 均等な分布を生成するキーが必要です(そうしないと、多くを逃し、ハッシュ以外の何かに依存する必要があります;線形検索のように)。
  • ハッシュは多くの空きスペースを使用できます。 256個のエントリを予約できますが、必要なのは8個までです(これまで)。

二分木:

  • 確定的。 O(log n)私は思う...
  • ハッシュテーブルができるような余分なスペースを必要としない
  • 並べ替えておく必要があります。中央に要素を追加すると、残りの部分が動きます。
6
whitey04

単一の要素にのみアクセスする必要がある場合は、ハッシュテーブルの方が優れています。ある範囲の要素が必要な場合、バイナリツリー以外のオプションはありません。

3
biziclop

上記の他の素晴らしい答えに追加するには、私は言うだろう:

データ量が変わらない場合は、ハッシュテーブルを使用します(定数の保存など)。ただし、データ量が変化する場合は、ツリーを使用してください。これは、ハッシュテーブルで負荷係数に達すると、ハッシュテーブルのサイズを変更する必要があるという事実によるものです。サイズ変更操作は非常に遅くなる可能性があります。

3
David Weiser

対処していないと思う点の1つは、データ構造がpersistentの場合にツリーがはるかに優れていることです。つまり、不変の構造です。標準のハッシュテーブル(つまり、リンクリストの単一の配列を使用するもの)は、テーブル全体を変更しない限り変更できません。これに関連する状況の1つは、2つの同時実行関数の両方にハッシュテーブルのコピーがあり、そのうちの1つがテーブルを変更する場合です(テーブルが変更可能な場合、その変更は他のテーブルにも表示されます)。別の状況は次のようなものです。

def bar(table):
    # some intern stuck this line of code in
    table["hello"] = "world"
    return table["the answer"]

def foo(x, y, table):
    z = bar(table)
    if "hello" in table:
        raise Exception("failed catastrophically!")
    return x + y + z

important_result = foo(1, 2, {
    "the answer": 5,
    "this table": "doesn't contain hello", 
    "so it should": "be ok"
})
# catastrophic failure occurs

可変テーブルでは、関数呼び出しが受け取るテーブルが実行中そのテーブルにとどまることを保証できません。他の関数呼び出しがテーブルを変更する可能性があるためです。

したがって、可変性は時には楽しいことではありません。現在、これを回避する方法は、テーブルを不変に保ち、更新が古いテーブルを変更せずにnewテーブルを返すようにすることです。しかし、ハッシュテーブルを使用すると、多くの場合、コストのかかるO(n)操作になります。基になる配列全体をコピーする必要があるためです。一方、バランスのとれたツリーでは、O(log n)ノードのみを作成する必要のある新しいツリーを生成できます(ツリーの残りの部分は同一です)。

これは、不変のマップが必要な場合に効率的なツリーが非常に便利になることを意味します。

2
limp_chimp

セットの多くのわずかに異なるインスタンスがある場合は、おそらく構造を共有する必要があります。これはツリーを使用すると簡単です(ツリーが不変またはコピーオンライトの場合)。ハッシュテーブルでどれだけうまくできるかわかりません。少なくともそれほど明白ではありません。

1
Darius Bacon

私の経験では、ツリーはキャッシュの影響をあまりにも受けやすいため、hastableは常に高速です。

実際のデータを確認するには、TommyDSライブラリのベンチマークページを確認してください http://tommyds.sourceforge.net/

ここでは、最も一般的なハッシュテーブル、ツリー、トライライブラリのパフォーマンスを比較できます。

1
amadvance

注意すべき1つのポイントは、トラバース、最小および最大アイテムについてです。ハッシュテーブルは、あらゆる種類の順序付きトラバース、または最小または最大アイテムへのアクセスをサポートしていません。これらの機能が重要な場合、バイナリツリーの方が適しています。

0