trueとradix trieのデータ構造は同じものですか?
それらが同じ場合、基数トライ(別名パトリシアトライ)の意味は何ですか?
基数ツリーは、トライの圧縮バージョンです。トライでは、各エッジで1文字を書きますが、パトリシアツリー(または基数ツリー)では、単語全体を保存します。
ここで、単語hello
、hat
およびhave
があると仮定します。それらをtrieに保存するには、次のようになります。
e - l - l - o
/
h - a - t
\
v - e
そして、9つのノードが必要です。ノードに文字を配置しましたが、実際にはエッジにラベルを付けます。
基数ツリーには、次のものがあります。
*
/
(Ello)
/
* - h - * -(a) - * - (t) - *
\
(ve)
\
*
必要なノードは5つだけです。上の図では、ノードはアスタリスクです。
したがって、全体として、基数ツリーはメモリ不足を取りますが、実装するのは困難です。それ以外の場合、両方のユースケースはほとんど同じです。
私の質問は、Trieデータ構造とRadix Trieが同じものかどうかです。
要するに、いいえ。カテゴリRadix TrieはTrieの特定のカテゴリを説明しますが、すべての試行が基数試行であることを意味しません。
それらが同じでない場合、Radix trie(別名Patricia Trie)の意味は何ですか?
質問にare n'tと書くつもりだったと思うので、私の修正です。
同様に、PATRICIAは特定のタイプの基数トライを示しますが、すべての基数試行がPATRICIA試行であるとは限りません。
「Trie」は、分岐またはエッジがキーのpartsに対応する連想配列としての使用に適したツリーデータ構造を示します。 partsの定義は、ここではかなりあいまいです。これは、試行の実装が異なると、エッジに対応するために異なるビット長が使用されるためです。たとえば、バイナリトライには0または1に対応するノードごとに2つのエッジがあり、16ウェイトライには4ビット(または16進数:0x0から0xf)に対応するノードごとに16のエッジがあります。
ウィキペディアから取得したこの図は、(少なくとも)キー「A」、「to」、「tea」、「ted」、「ten」、「inn」が挿入されたトライを示しているようです。
このトライがキー「t」、「te」、「i」、または「in」のアイテムを保存する場合、ヌルノードと実際の値を持つノードを区別するために、各ノードに追加情報が必要です。
「Radix trie」は、Ivaylo Strandjevが答えで説明したように、一般的なプレフィックス部分を凝縮するトライの形式を説明しているようです。次の静的な割り当てを使用して、キー「smile」、「smiled」、「smiles」、および「smiling」にインデックスを付ける256通りのトライを考えてください。
root['s']['m']['i']['l']['e']['\0'] = smile_item;
root['s']['m']['i']['l']['e']['d']['\0'] = smiled_item;
root['s']['m']['i']['l']['e']['s']['\0'] = smiles_item;
root['s']['m']['i']['l']['i']['n']['g']['\0'] = smiling_item;
各添え字は内部ノードにアクセスします。つまり、smile_item
を取得するには、7つのノードにアクセスする必要があります。 8つのノードアクセスはsmiled_item
およびsmiles_item
に対応し、9つはsmiling_item
に対応します。これらの4つのアイテムには、合計14個のノードがあります。ただし、それらはすべて共通の最初の4バイト(最初の4つのノードに対応)を持っています。これらの4バイトを圧縮して['s']['m']['i']['l']
に対応するroot
を作成することにより、4つのノードアクセスが最適化されました。これは、メモリとノードへのアクセスが少なくなることを意味し、非常に良い兆候です。最適化を再帰的に適用して、不要なサフィックスバイトにアクセスする必要性を減らすことができます。最終的に、トライによってインデックスが付けられた場所の検索キーとインデックス付けされたキーの違いのみを比較するポイントに到達します。これは基数トライです。
root = smil_dummy;
root['e'] = smile_item;
root['e']['d'] = smiled_item;
root['e']['s'] = smiles_item;
root['i'] = smiling_item;
アイテムを取得するには、各ノードに位置が必要です。 「smiles」の検索キーと4のroot.position
を使用して、root["smiles"[4]]
にアクセスします。これはたまたまroot['e']
です。これをcurrent
という変数に保存します。 current.position
は5です。これは"smiled"
と"smiles"
の違いの場所であるため、次のアクセスはroot["smiles"[5]]
になります。これにより、smiles_item
と文字列の終わりに到達します。私たちの検索は終了し、アイテムは8つではなく3つのノードアクセスで取得されました。
PATRICIAトライは、n
アイテムを含むために使用されるn
ノードのみが存在する基数試行の変形です。上記の大雑把に示した基数トライ擬似コードには、合計で5つのノードがあります:root
(nullaryノードです。実際の値は含まれません)、root['e']
、root['e']['d']
、root['e']['s']
およびroot['i']
。パトリシアのトライでは、4人しかいません。 PATRICIAはバイナリアルゴリズムであるため、これらのプレフィックスがバイナリでどのように異なるかを見てみましょう。
smile: 0111 0011 0110 1101 0110 1001 0110 1100 0110 0101 0000 0000 0000 0000
smiled: 0111 0011 0110 1101 0110 1001 0110 1100 0110 0101 0110 0100 0000 0000
smiles: 0111 0011 0110 1101 0110 1001 0110 1100 0110 0101 0111 0011 0000 0000
smiling: 0111 0011 0110 1101 0110 1001 0110 1100 0110 1001 0110 1110 0110 0111 ...
ノードが上記の順序で追加されると考えてみましょう。 smile_item
はこのツリーのルートです。違いを見つけやすくするために太字で示していますが、"smile"
の最後のバイトのビット36にあります。この時点まで、すべてのノードには同じプレフィックスが付いています。 smiled_node
はsmile_node[0]
に属します。 "smiled"
と"smiles"
の違いはビット43で発生します。"smiles"
には「1」ビットがあるため、smiled_node[1]
はsmiles_node
です。
NULL
をブランチおよび/または追加の内部情報として使用して検索の終了を示すのではなく、ブランチはリンクを戻しますpどこかのツリーなので、テストするオフセットが終わると検索が終了します- 減少増加するのではなく。以下に、そのようなtreeの簡単な図を示します(パトリシアは、実際には、ツリーよりも循環グラフに近いものです)。これは、下記のSedgewickの本に含まれています。
可変長のキーを含むより複雑なPATRICIAアルゴリズムが可能ですが、PATRICIAの技術的特性の一部はプロセスで失われます(つまり、すべてのノードにはその前のノードと共通のプレフィックスが含まれます)。
このように分岐することには、いくつかの利点があります。すべてのノードには値が含まれます。これにはルートが含まれます。その結果、コードの長さと複雑さは大幅に短くなり、実際にはおそらく少し速くなります。少なくとも1つのブランチと最大でk
ブランチ(k
は検索キーのビット数)をたどって、アイテムを見つけます。ノードはtinyです。それぞれ2つのブランチのみを格納するため、キャッシュの局所性の最適化にかなり適しています。これらの特性により、これまでPATRICIAは私のお気に入りのアルゴリズムになりました...
差し迫った関節炎の重症度を軽減するために、ここではこの説明を短くしますが、パトリシアについて詳しく知りたい場合は、ドナルドクヌースの「コンピュータプログラミング、第3巻」などの本を参照してください。 、またはSedgewickによる「{your-favourite-language}、parts 1-4」のいずれかのアルゴリズム。
トライ:
検索スキーム全体を検索キーと既存のすべてのキー(ハッシュスキームなど)と比較する代わりに、検索キーの各文字を比較することもできます。このアイデアに従って、3つの既存のキーを持つ構造を構築できます(以下に示す)。「dad」、「dab」、および「cab」。
[root]
...// | \\...
| \
c d
| \
[*] [*]
...//|\. ./|\\... Fig-I
a a
/ /
[*] [*]
...//|\.. ../|\\...
/ / \
B b d
/ / \
[] [] []
(cab) (dab) (dad)
これは基本的に、[*]で表される内部ノードと[]で表されるリーフノードを持つM-aryツリーです。この構造はtrieと呼ばれます。各ノードでの分岐決定は、アルファベットの一意のシンボルの数、たとえばRと等しくすることができます。小文字の英語のアルファベットa〜zの場合、R = 26。拡張ASCIIアルファベットの場合、R = 256、2進数字/文字列の場合R = 2。
コンパクトTRIE:
通常、trieのノードは、size = Rの配列を使用するため、各ノードのエッジが少ない場合にメモリの無駄が生じます。メモリの問題を回避するために、さまざまな提案がなされました。これらのバリエーションに基づいて、trieは「compact trie」および「compressed trie」とも呼ばれます。一貫した命名法はまれですが、コンパクトなtrieの最も一般的なバージョンは、ノードに単一のEdgeがある場合にすべてのEdgeをグループ化することによって形成されます。この概念を使用すると、上記の(図-I)trieキーが「dad」、「dab」、および「cab」の場合、次の形式を取ることができます。
[root]
...// | \\...
| \
cab da
| \
[ ] [*] Fig-II
./|\\...
| \
b d
| \
[] []
「c」、「a」、「b」はそれぞれ、対応する親ノードの唯一のエッジであるため、単一のエッジ「cab」にまとめられていることに注意してください。同様に、「d」と「a」は「da」というラベルの付いた単一のEdgeにマージされます。
Radix Trie:
数学のradixという用語は、数値システムの基数を意味し、基本的にそのシステムの任意の数値を表すために必要な一意のシンボルの数を示します。たとえば、10進法は基数10、2進法は基数2です。同様の概念を使用して、基になる表現システムの一意のシンボルの数によってデータ構造またはアルゴリズムを特徴付けることに関心がある場合、用語に「基数」というタグを付けます。たとえば、特定のソートアルゴリズムの「基数ソート」。ロジックの同じ行で、特性(深さ、メモリの必要性、検索ミス/ヒットランタイムなど)が基になる基数に依存するtrieのすべてのバリアントアルファベットの場合、基数を「トライ」と呼びます。たとえば、非圧縮および圧縮trieアルファベットa〜zを使用する場合、基数26と呼ぶことができますtrie =。 2つのシンボル(従来は「0」と「1」)のみを使用するトライは、基数2 トライと呼ぶことができます。ただし、圧縮されたtrieに対してのみ、「Radix Trie」という用語の使用を制限している文献がいくつかあります。
パトリシアツリー/トライの前奏曲
キーとしての文字列でさえ、バイナリアルファベットを使用して表現できることに気付くのは興味深いでしょう。 ASCIIエンコーディングと仮定すると、キー「dad」は、各文字のバイナリ表現を「11001-」のように順番に記述することにより、バイナリ形式で記述できます。 110000111001”バイナリ形式の「d」、「a」、「d」を順番に書き込むことにより、この概念を使用して、a trie(基数2で)を形成できます。以下では、文字 'a'、 'b'、 'c'、および 'd'がASCIIの代わりに小さいアルファベットからのものであるという単純な仮定を使用してこの概念を示します。
図IIIの注:前述のように、説明を簡単にするために、アルファベットが4文字{a、b、c、d}のみで、対応するバイナリ表現が「00」、「01」、それぞれ「10」と「11」。これにより、文字列キー「dad」、「dab」、「cab」はそれぞれ「110011」、「110001」、「100001」になります。このトライは、図-IIIに示すようになります(文字列が左から右に読み取られるようにビットは左から右に読み取られます)。
[root]
\1
\
[*]
0/ \1
/ \
[*] [*]
0/ /
/ /0
[*] [*]
0/ /
/ /0
[*] [*]
0/ 0/ \1 Fig-III
/ / \
[*] [*] [*]
\1 \1 \1
\ \ \
[] [] []
(cab) (dab) (dad)
パトリシアトライ/ツリー:
シングルエッジコンパクションを使用して上記のバイナリtrie(図III)を圧縮すると、上記のノードよりもはるかに少ないノードになりますが、ノードは含まれるキーの数である3よりも多くなります。 Donald R. Morrison(1968年)バイナリを使用する革新的な方法が見つかりましたtrie N個のノードと彼はこのデータ構造に名前を付けました[〜#〜] patricia [〜#〜]。彼のトライ構造は、本質的に単一のエッジ(一方向の分岐)を取り除きました。また、そうすることで、2種類のノードの概念を取り除きました。内部ノード(キーを表示しない)とリーフノード(キーを表示する)です。上記で説明したコンパクションロジックとは異なり、彼のトライでは、分岐決定を行うためにスキップするキーのビット数を各ノードに含めるという異なる概念を使用しています。彼のPATRICIAトライのもう1つの特徴は、キーを保存しないことです。つまり、このようなデータ構造は、特定のプレフィックスに一致するすべてのキーをリストするのような質問に答えるのには適しませんキーがトライに存在するかどうかを見つけます。それにもかかわらず、パトリシアツリーまたはパトリシアトライという用語は、その後、コンパクトトライ[NIST]を示すため、または基数2を持つ基数トライを示すために(微妙に示されているように) WIKIでの方法]など。
基数トライではない可能性のあるトライ:
Ternary Search Trie(別名Ternary Search Tree)-[〜#〜] tst [〜#〜]と略されることが多いデータ構造(J。BentleyおよびR。Sedgewickで提案)。これは、3分岐のトライに非常に似ています。そのようなツリーの場合、各ノードには特徴的なアルファベット「x」があり、キーの文字が「x」よりも小さいか、等しいか、大きいかによって分岐の決定が行われます。この固定された3方向分岐機能により、特にUnicodeアルファベットなどのR(基数)が非常に大きい場合、トライのメモリ効率の高い代替手段が提供されます。興味深いことに、(R-way)trieとは異なり、TSTにはRの影響を受ける特性がありません。たとえば、TSTの検索ミスはln (N)反対logR(N) R-way Trieの場合。 R-wayとは異なり、TSTのメモリ要件trieは[〜#〜] not [〜#〜] Rの関数でもあります。そのため、TSTを基数トライと呼ぶことに注意する必要があります。個人的には、基数のアルファベットの基数Rの影響を受けないため(私が知る限り)、基数トライと呼ぶべきではないと思います。