web-dev-qa-db-ja.com

ハッシュテーブルとトライ(プレフィックスツリー)を選択するにはどうすればよいですか?

そのため、ハッシュテーブルまたはプレフィクスツリーのどちらかを選択する必要がある場合、どちらを選択するかを決定する要因は何ですか。私自身の素朴な観点から、トライを使用すると配列として保存されないため、余分なオーバーヘッドがありますが、実行時間の観点では(最長のキーが最長の英語の単語であると仮定して)、本質的に= O(1)(上限に関連して)。おそらく最も長い英語の単語は50文字ですか?

ハッシュテーブルは即座に検索されますインデックスを取得したら。ただし、キーをハッシュしてインデックスを取得すると、50ステップ近くを簡単に実行できるように思えます。

誰かがこれについてより経験豊富な視点を提供できますか?ありがとう!

127
Justin Bozonier

試行の利点:

基礎:

  • 予測可能O(k)ルックアップ時間(kはキーのサイズ)
  • 参照がない場合、検索にかかる時間はk未満です。
  • 順序付きトラバーサルをサポート
  • ハッシュ関数は不要
  • 削除は簡単です

新しい操作:

  • キーのプレフィックスをすばやく検索したり、特定のプレフィックスを持つすべてのエントリを列挙したりできます。

リンク構造の利点:

  • 共通のプレフィックスが多数ある場合、それらに必要なスペースは共有されます。
  • 不変の試行は構造を共有できます。適切なトライを更新する代わりに、1つのブランチに沿ってのみ異なる新しいトライを作成できます。これは、同時実行、テーブルの複数の同時バージョンなどに役立ちます。
  • 不変のトライは圧縮可能です。つまり、ハッシュ=コンシングによって、サフィックスの構造も共有できます。

ハッシュテーブルの利点:

  • 誰もがハッシュテーブルを知っていますよね?お使いのシステムには、すでに最適化されたニースの実装がすでにあり、ほとんどの目的で試行するよりも高速です。
  • キーには特別な構造は必要ありません。
  • リンクされた明白なトライ構造よりもスペース効率が高い(以下のコメントを参照
113
Darius Bacon

それはすべてあなたが解決しようとしている問題に依存します。挿入と検索だけが必要な場合は、ハッシュテーブルを使用します。プレフィックス関連のクエリなど、より複雑な問題を解決する必要がある場合は、トライがより良い解決策である可能性があります。

45
Adam Rosenfield

誰もがハッシュテーブルとその使用法を知っていますが、それは正確に一定のルックアップ時間ではなく、ハッシュテーブルの大きさ、ハッシュ関数の計算の複雑さに依存します。

効率的なルックアップのために巨大なハッシュテーブルを作成することは、小さなレイテンシ/スケーラビリティが重要な産業シナリオ(たとえば、高頻度の取引)のほとんどでエレガントなソリューションではありません。キャッシュミスを減らすには、メモリ内で占有するスペースに対して最適化されるデータ構造に注意する必要があります。

トライが要件により適している非常に良い例は、メッセージングミドルウェアです。さまざまなカテゴリのメッセージのサブスクライバーおよびパブリッシャー(JMSの用語-トピックまたは交換)が100万人いる場合、トピック(実際は文字列)に基づいてメッセージをフィルターで除外する場合は、ハッシュテーブルを作成する必要はありません100万のトピックを持つ100万のサブスクリプションのために。より良いアプローチは、トピックをトライで保存することです。そのため、トピックの一致に基づいてフィルタリングが行われる場合、その複雑さはトピック/サブスクリプション/パブリッシャーの数に依存しません(文字列の長さにのみ依存します)。スペースの要件を最適化するためにこのデータ構造を使用して創造性を高め、キャッシュミスを減らすことができるため、気に入っています。

26
user179156

ツリーを使用する:

  1. オートコンプリート機能が必要な場合
  2. 「a」または「axe」などで始まるすべての単語を検索します。
  3. 接尾辞ツリーは、ツリーの特別な形式です。サフィックスツリーには、ハッシュではカバーできない利点の全リストがあります。
8
Dr.Sai

誰も明示的に言及していないが、心に留めておくことが重要だと思うものがあります。ハッシュテーブルとさまざまな種類の試行の両方に、通常O(k)操作があります。ここで、kは文字列の長さ(ビット単位、または同等の文字単位)です。

これは、適切なハッシュ関数があることを前提としています。 「農場」と「農場の動物」を同じ値にハッシュしたくない場合、ハッシュ関数はキーのすべてのビットを使用する必要があるため、「農場の動物」のハッシュには約2倍の時間がかかります「農場」(何らかのローリングハッシュシナリオを使用している場合を除きますが、操作を節約するためのいくつかのシナリオも試行されます)。そして、バニラの試みで、「農場の動物」を挿入するのに「農場」の約2倍の時間がかかるのは明らかです。長期的には、圧縮された試行でも同様です。

2
user3391564

トライへの挿入とルックアップは、入力文字列O(s)の長さと線形です。

ハッシュは、ルックアップと挿入のためにO(1)を提供しますが、最初に再びO(s)である入力文字列に基づいてハッシュを計算する必要があります。

結論として、漸近的な時間の複雑さはどちらの場合も線形です。

トライにはデータの観点からいくつかのオーバーヘッドがありますが、圧縮されたトライを選択すると、ハッシュテーブルとほぼ同程度になります。

ネクタイを破るには、この質問を自問してください:完全な単語だけを検索する必要がありますか?または、プレフィックスに一致するすべての単語を返す必要がありますか? (予測テキスト入力システムと同様)。最初のケースでは、ハッシュを探します。よりシンプルでクリーンなコードです。テストと保守がより簡単に。接頭辞または接尾辞が重要である、より詳細な使用例については、試してみてください。

そして、もしあなたが楽しみのためにそれをするなら、トライを実装することは日曜日の午後を有効に使うでしょう。

2
Visiedo

HashTable実装は、基本的なTrie実装と比較してスペース効率が高くなります。しかし、文字列では、ほとんどの実際のアプリケーションで順序付けが必要です。ただし、HashTableは辞書的な順序を完全に乱します。アプリケーションが辞書式順序(部分検索、特定のプレフィックスを持つすべての文字列、すべての単語を並べ替えられた順序など)に基づいて操作している場合、トライを使用する必要があります。ルックアップのみの場合、HashTableを使用する必要があります(ほぼ間違いなく、最短のルックアップ時間を提供します)。

P.S.:これら以外に、三項探索木(TST)が優れた選択肢です。ルックアップ時間はHashTableよりも長くなりますが、他のすべての操作では時間効率が高くなります。また、試行よりもスペース効率が高くなります。

2
Jay Jodiwal