web-dev-qa-db-ja.com

ハッシュテーブルからエントリを削除する最良の方法

線形プロービングを使用するハッシュテーブルからエントリを削除する最良の方法は何ですか?これを行う1つの方法は、削除された要素を示すためにフラグを使用することです。これより良い方法はありますか?

23
ashokgelal

簡単なテクニックは次のとおりです。

  1. 目的の要素を見つけて削除します
  2. 次のバケットに移動
  3. バケットが空の場合は終了します
  4. バケットがいっぱいの場合は、そのバケット内の要素を削除し、通常の方法を使用してハッシュテーブルに再度追加します。アイテムは元の場所に追加される可能性があるため、再追加す​​る前にアイテムを削除する必要があります。
  5. 手順2を繰り返します。

この手法により、削除が少し遅くなりますが、テーブルが整頓されます。

33
Imbue

オーバーフローの処理方法と、(1)削除されるアイテムがオーバーフロースロットにあるかどうか、および(2)削除されるアイテム以外にオーバーフローアイテムがあるかどうか、削除されるアイテムのハッシュキーがあるかどうかによって異なります。またはおそらく他のハッシュキー。 [二重条件を見落とすことは、削除の実装におけるバグの一般的な原因です。]

衝突がリンクリストにオーバーフローした場合、それは非常に簡単です。リストをポップアップするか(空になっている可能性があります)、リンクリストの中央または最後からメンバーを削除しています。それらは楽しく、特に難しいことではありません。これをさらに効率的にするために、過剰なメモリ割り当てと解放を回避するための他の最適化があります。

線形プロービングの場合、クヌースは、スロットを空、削除、または占有としてマークする方法を用意するのが簡単なアプローチであると提案しています。削除された占有スロットを削除済みとしてマークして、線形プロービングによるオーバーフローがそれをスキップするようにしますが、挿入が必要な場合は、渡した最初の削除されたスロットを埋めることができます[The Art of Computer Programming、vol.3:Sorting and Searching 、セクション6.4ハッシュ、p。 533(ed.2)]。これは、削除がかなりまれであることを前提としています。

クヌースはアルゴリズムR6.4 [pp。 533-534]代わりに、セルを削除ではなく空としてマークし、作成したばかりの穴を別の穴の隣に移動することで、テーブルエントリを初期プローブの場所に戻す方法を見つけます。

Knuthは、これにより既存のまだ占有されているスロットエントリが移動するため、スロットへのポインタがハッシュテーブルの外側に保持されている場合はお勧めできません。 [スロットにガベージコレクションまたはその他の管理された参照がある場合、それはテーブルの外で使用されている参照であり、参照するスロットがどこにあるかは問題ではないため、スロットを移動しても問題ありません。同じオブジェクトがテーブルにあります。]

14
orcmid

Pythonハッシュテーブルの実装(非常に高速であると言えます)は、ダミー要素を使用して削除をマークします。テーブルを拡大または縮小すると(固定サイズのテーブルを実行していないと仮定)、削除できます。同時にダミー。

コピーにアクセスできる場合は、実装に関する ビューティフルコード の記事を参照してください。

9
Tony Arkles

私が考えることができる最も一般的な解決策は次のとおりです。

  • 非定数イテレータ(ala C++ STLまたはJava)を使用できる場合は、それらに遭遇したときにそれらを削除できるはずです。ただし、基になるコレクションが変更された場合に無効になる定数イテレータまたは列挙子を使用していない限り、おそらくこの質問をすることはありません。
  • あなたが言ったように、あなたは含まれているオブジェクト内で削除されたフラグをマークすることができます。ただし、これによってメモリが解放されたり、キーの衝突が減ったりすることはないため、最善の解決策ではありません。また、おそらく実際にはそこに属していないクラスにプロパティを追加する必要があります。これが私と同じくらい気になる場合、または単に格納されたオブジェクトにフラグを追加できない場合(おそらくクラスを制御しない場合)、これらのフラグを別のハッシュテーブルに格納できます。これには、最も長期間のメモリ使用が必要です。
  • ハッシュテーブルをトラバースしながら、削除するアイテムのキーをベクトルまたは配列リストにプッシュします。列挙子を解放した後、この2次リストをループして、ハッシュテーブルからキーを削除します。削除するアイテムがたくさんある場合や、キーが大きい場合(そうすべきではありません)、これは最善の解決策ではない可能性があります。
  • ハッシュテーブルに残しておくよりも多くのアイテムをハッシュテーブルから削除することになった場合は、新しいハッシュテーブルを作成し、元のハッシュテーブルをトラバースするときに、新しいハッシュテーブルにのみ追加する方がよい場合があります。保管するアイテム。次に、古いハッシュテーブルへの参照を新しいものに置き換えます。これにより、セカンダリリストの反復が節約されますが、新しいハッシュテーブルのアイテム数が元のハッシュテーブルよりも大幅に少ない場合にのみ効率的であり、もちろん、元のハッシュテーブルへのすべての参照を変更できる場合にのみ機能します。
  • ハッシュテーブルからキーのコレクションにアクセスできる場合は、それらを繰り返し処理して、1回のパスでハッシュテーブルからアイテムを削除できる場合があります。
  • ハッシュテーブルまたはライブラリ内のヘルパーが述語ベースのコレクション修飾子を提供する場合、削除するアイテムを識別するためにラムダ式または関数ポインターを渡すことができるRemove()関数がある場合があります。
3
P Daddy

時間が要因である場合の一般的な手法は、削除されたアイテムの2番目のテーブルを用意し、時間があるときにメインテーブルをクリーンアップすることです。一般的に検索エンジンで使用されます。

1
Stephen

リンクリストのようなポインタを含むようにハッシュテーブルを拡張するのはどうですか?挿入時にバケットがいっぱいの場合は、このバケットから新しいフィールドが格納されているバケットへのポインタを作成します。

ハッシュテーブルから何かを削除している間、解決策は、リンクリストからノードを削除する関数を作成する方法と同等になります。

0
user892871