web-dev-qa-db-ja.com

チェーンハッシュテーブルとオープンアドレスハッシュテーブル

誰かが2つの実装の(利点/欠点)の主な違いを説明できますか?

ライブラリの場合、どの実装が推奨されますか?

47
Andrei Ciobanu

ハッシュテーブルに関するウィキペディアの記事 は、人々が使用したさまざまなハッシュテーブルスキームの説明と概要を、私が頭で理解するよりも明らかに優れています。実際、ここで質問するよりも、おそらくその記事を読む方が得策です。 :)

それは言った...

連鎖ハッシュテーブルは、リンクリストの先頭へのポインターの配列にインデックスを付けます。各リンクリストセルには、割り当てられたキーと、そのキーに挿入された値があります。キーから特定の要素を検索する場合、キーのハッシュを使用して、どのリンクリストをたどるかを調べ、その特定のリストをたどって、目的の要素を見つけます。ハッシュテーブル内の複数のキーが同じハッシュを持っている場合は、複数の要素を持つリンクリストが作成されます。

連鎖ハッシュの欠点は、リンクされたリストを検索するためにポインタを追跡する必要があることです。利点は、チェーンハッシュテーブルは、負荷係数(ハッシュテーブルの要素とバケット配列の長さの比率)が1を超えても、増加するにつれて線形的に遅くなることだけです。

オープンアドレス指定のハッシュテーブルは、(キー、値)のペアへのポインターの配列にインデックスを付けます。キーのハッシュ値を使用して、配列内のどのスロットを最初に調べるかを決定します。ハッシュテーブル内の複数のキーが同じハッシュを持っている場合、代わりに検索する別のスロットを決定するスキームを使用します。たとえば、線形プローブとは、選択したスロットの次のスロット、次にその次のスロットの順に調べるというもので、探しているキーと一致するスロットが見つかるまで、または空のスロット(その場合、キーはそこに存在してはなりません)。

リストノード間のポインタを追跡する必要がないため、負荷係数が低い場合、通常、オープンアドレッシングはチェーンハッシュよりも高速です。負荷係数が1に近づくと、非常に遅くなります。通常、探していたキーまたは空のスロットを見つける前に、バケット配列の多くのスロットを検索する必要があるためです。また、バケット配列にエントリがあるよりも多くの要素をハッシュテーブルに含めることはできません。

負荷係数が1に近づくと、すべてのハッシュテーブルが少なくとも遅くなる(そして場合によっては実際に完全に壊れる)という事実に対処するために、実際のハッシュテーブルの実装では、バケット配列を大きくします(新しいバケット配列を割り当て、古いものを新しいものに置き換え、次に古いものを解放します)、負荷係数が特定の値(通常は約0.7)を超えたとき。

上記のすべてのバリエーションがたくさんあります。繰り返しになりますが、ウィキペディアの記事をご覧ください。非常に優れています。

他の人が使用することを意図したライブラリーについては、強く実験することをお勧めします。それらは一般に非常にパフォーマンスが非常に重要であるため、通常はすでに慎重に調整されたハッシュテーブルの誰か他の実装を使用するのが最善です。オープンソースのBSD、LGPL、およびGPLライセンスのハッシュテーブル実装がたくさんあります。

たとえば、GTKを使用している場合は、優れた GLibのハッシュテーブル があることがわかります。

56
Richard Barrell

優れた説明が与えられているので、CLRSから取得した視覚化を追加してさらに説明します。

オープンアドレッシング: Open Addressing:

連鎖: Chaining:

2
oba2311

ほとんどのライブラリーはチェーン戦略を使用していますが、私の理解は(簡単に言えば)どちらの方法にも長所と短所があることです。

連鎖方法:

ここで、ハッシュテーブルの配列は、リンクされたアイテムのリストにマップされます。これは、衝突の数がかなり少ない場合に効率的です。最悪のシナリオはO(n)です。ここで、nはテーブル内の要素の数です。

線形プローブを使用したアドレス指定を開く:

ここで衝突が発生したら、空いている場所が見つかるまで次のインデックスに移動します。したがって、衝突の数が少ない場合、これは非常に高速でスペース効率が良いです。ここでの制限は、テーブル内のエントリの総数が配列のサイズによって制限されることです。これは連鎖には当てはまりません。

二分探索木で連鎖するという別のアプローチがあります。このアプローチでは、衝突が発生すると、リンクリストではなくバイナリ検索ツリーに格納されます。したがって、ここでの最悪のシナリオはO(log n)です。実際には、このアプローチは、非常に不均一な分布がある場合に最適です。

1
YuVi

オープンアドレスと個別のチェーン

キーがハッシュテーブル自体のエントリとして保持されている場合、線形プローブ、ダブルハッシュ、ランダムハッシュが適切です。これを「オープンアドレッシング」と呼び、「クローズハッシュ」とも呼びます。

別のアイデア:ハッシュテーブルのエントリは、リンクされたリスト(「チェーン」)の先頭へのポインタにすぎません。リンクされたリストの要素にはキーが含まれています...これは「個別チェーン」と呼ばれ、「オープンハッシュ」とも呼ばれます

個別のチェーンを使用すると、衝突の解決が簡単になります。リンクされたリストにキーがない場合は、キーを挿入するだけです(リンクされたリストよりも優れたデータ構造を使用できますが、リンクされたリストは、平均的なケースで非常にうまく機能します。これらの戦略の時間コストの分析を見てみましょう

出典: http://cseweb.ucsd.edu/~kube/cls/100/Lectures/lec16/lec16-25.html

0
Santosh Sindham