it-swarm.com.de

Was ist der Unterschied zwischen Trie- und Radix-Trie-Datenstrukturen?

Sind die Datenstrukturen trie und radix trie dasselbe?

Wenn sie gleich sind, was bedeutet dann radix trie (AKA Patricia trie)?

82
Daggerhunt

Ein Radix-Baum ist eine komprimierte Version eines Tries. In einem Trie schreiben Sie an jeder Kante einen einzelnen Buchstaben, während Sie in einem PATRICIA-Baum (oder einem Radix-Baum) ganze Wörter speichern.

Angenommen, Sie haben die Wörter hello, hat und have. Um sie in einem trie zu speichern, würde es so aussehen:

    e - l - l - o
  /
h - a - t
      \
       v - e

Und du brauchst neun Knoten. Ich habe die Buchstaben in die Knoten gelegt, aber tatsächlich beschriften sie die Kanten.

In einem Radixbaum haben Sie:

            *
           /
        (Ello)
         /
* - h - * -(a) - * - (t) - *
                 \
                 (ve)
                   \
                    *

und Sie brauchen nur fünf Knoten. In der obigen Abbildung sind die Knoten die Sternchen.

Insgesamt benötigt ein Radix-Baum also weniger Speicher, aber es ist schwieriger zu implementieren. Ansonsten ist der Anwendungsfall von beiden ziemlich gleich.

105

Meine Frage ist, ob Trie Datenstruktur und Radix Trie dasselbe sind?

Kurz gesagt, nein. Die Kategorie Radix Trie beschreibt eine bestimmte Kategorie von Trie, aber das bedeutet nicht, dass alle Versuche Radix-Versuche sind.

Wenn sie [nicht] gleich sind, was bedeutet dann Radix Trie (aka Patricia Trie)?

Ich gehe davon aus, dass Sie nicht in Ihre Frage schreiben wollten, daher meine Korrektur.

Ebenso bezeichnet PATRICIA eine bestimmte Art von Radix-Versuchen, aber nicht alle Radix-Versuche sind PATRICIA-Versuche.


Was ist ein Versuch?

"Trie" beschreibt eine Baumdatenstruktur, die zur Verwendung als assoziatives Array geeignet ist, wobei Zweige oder Kanten Teile eines Schlüssels entsprechen. Die Definition von parts ist hier ziemlich vage, da unterschiedliche Implementierungen von Versuchen unterschiedliche Bitlängen verwenden, um Kanten zu entsprechen. Beispielsweise hat ein binärer Trie zwei Kanten pro Knoten, die einer 0 oder einer 1 entsprechen, während ein 16-Wege-Trie sechzehn Kanten pro Knoten hat, die vier Bits entsprechen (oder eine hexadezimale Ziffer: 0x0 bis 0xf).

Dieses Diagramm, das aus Wikipedia stammt, scheint einen Versuch zu zeigen, bei dem (mindestens) die Schlüssel 'A', 'to', 'tea', 'ted', 'ten' und 'inn' eingefügt wurden:

Basic trie

Wenn in diesem Versuch Elemente für die Schlüssel 't', 'te', 'i' oder 'in' gespeichert würden, müssten an jedem Knoten zusätzliche Informationen vorhanden sein, um zwischen Nullknoten und Knoten mit tatsächlichen Werten zu unterscheiden.


Was ist ein Radix-Trie?

"Radix trie" scheint eine Form von Trie zu beschreiben, die gemeinsame Präfixteile verdichtet, wie Ivaylo Strandjev in seiner Antwort beschrieben hat. Angenommen, ein 256-Wege-Versuch, der die Schlüssel "smile", "smiled", "smiles" und "smiling" mit den folgenden statischen Zuweisungen indiziert:

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;

Jeder Index greift auf einen internen Knoten zu. Das heißt, um smile_item Abzurufen, müssen Sie auf sieben Knoten zugreifen. Acht Knotenzugriffe entsprechen smiled_item Und smiles_item Und neun entsprechen smiling_item. Für diese vier Elemente gibt es insgesamt vierzehn Knoten. Sie alle haben jedoch die ersten vier Bytes (entsprechend den ersten vier Knoten) gemeinsam. Durch die Verdichtung dieser vier Bytes zu einem root, das ['s']['m']['i']['l'] Entspricht, wurden vier Knotenzugriffe optimiert. Das bedeutet weniger Speicher und weniger Knotenzugriffe, was ein sehr guter Hinweis ist. Die Optimierung kann rekursiv angewendet werden, um den Zugriff auf unnötige Suffix-Bytes zu reduzieren. Irgendwann kommt man zu einem Punkt, an dem man nur noch vergleicht nterschiede zwischen dem Suchschlüssel und den indizierten Schlüsseln an Stellen, die vom Trie indiziert wurden. Dies ist eine radix Trie.

root = smil_dummy;
root['e'] = smile_item;
root['e']['d'] = smiled_item;
root['e']['s'] = smiles_item;
root['i'] = smiling_item;

Zum Abrufen von Elementen benötigt jeder Knoten eine Position. Mit einem Suchschlüssel von "lächelt" und einem root.position Von 4 greifen wir auf root["smiles"[4]] Zu, was zufällig root['e'] Ist. Wir speichern dies in einer Variablen namens current. current.position Ist 5, das ist der Ort der Differenz zwischen "smiled" Und "smiles", Der nächste Zugriff ist also root["smiles"[5]]. Dies bringt uns zu smiles_item Und dem Ende unserer Zeichenkette. Unsere Suche wurde abgebrochen und der Artikel wurde mit nur drei statt acht Knotenzugriffen abgerufen.


Was ist ein PATRICIA-Versuch?

Ein PATRICIA-Versuch ist eine Variante von Radix-Versuchen, für die es immer nur n Knoten geben sollte, die n Elemente enthalten. In unserem grob demonstrierten Radix-Trie-Pseudocode oben gibt es insgesamt fünf Knoten: root (das ist ein Nullknoten; es enthält keinen tatsächlichen Wert), root['e'], root['e']['d'], root['e']['s'] Und root['i']. In einem PATRICIA-Versuch sollten es nur vier sein. Werfen wir einen Blick darauf, wie sich diese Präfixe unterscheiden können, indem wir sie in Binärform betrachten, da PATRICIA ein binärer Algorithmus ist.

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 ...

Angenommen, die Knoten werden in der oben angegebenen Reihenfolge hinzugefügt. smile_item Ist die Wurzel dieses Baumes. Der Unterschied, der fett gedruckt ist, um das Erkennen zu erleichtern, befindet sich im letzten Byte von "smile" Bei Bit 36. Bis zu diesem Punkt haben alle unsere Knoten das Präfix dasselbe. smiled_node Gehört zu smile_node[0]. Der Unterschied zwischen "smiled" Und "smiles" Tritt bei Bit 43 auf, wobei "smiles" Ein "1" -Bit hat, also ist smiled_node[1]smiles_node.

Anstatt NULL als Verzweigung und/oder zusätzliche interne Information zu verwenden, um anzuzeigen, wann eine Suche beendet wird, verlinken die Verzweigungen zurück nach oben den Baum irgendwo, so dass eine Suche endet, wenn der Versatz zum Testen - nimmt ab statt zuzunehmen. Hier ist ein einfaches Diagramm eines solchen Baum (obwohl PATRICIA in Wirklichkeit eher ein zyklischer Graph als ein Baum ist, wie Sie sehen werden), das in Sedgewicks unten erwähntem Buch enthalten war:

Simple PATRICIA diagram

Ein komplexerer PATRICIA-Algorithmus mit Schlüsseln unterschiedlicher Länge ist möglich, obwohl einige der technischen Eigenschaften von PATRICIA dabei verloren gehen (dh, dass jeder Knoten ein gemeinsames Präfix mit dem vorherigen Knoten enthält):

Complex PATRICIA diagram

Eine solche Verzweigung bietet eine Reihe von Vorteilen: Jeder Knoten enthält einen Wert. Das schließt die Wurzel ein. Infolgedessen wird die Länge und Komplexität des Codes viel kürzer und in der Realität wahrscheinlich etwas schneller. Mindestens einer Verzweigung und höchstens k Verzweigungen (wobei k die Anzahl der Bits im Suchschlüssel ist) wird gefolgt, um ein Element zu lokalisieren. Die Knoten sind winzig, weil sie jeweils nur zwei Zweige speichern, was sie für die Optimierung der Cache-Lokalität ziemlich geeignet macht. Diese Eigenschaften machen PATRICIA bisher zu meinem Lieblingsalgorithmus ...

Ich werde diese Beschreibung hier kurz fassen, um die Schwere meiner bevorstehenden Arthritis zu verringern, aber wenn Sie mehr über PATRICIA erfahren möchten, können Sie Bücher wie "Die Kunst der Computerprogrammierung, Band 3" von Donald Knuth konsultieren , oder einen der "Algorithmen in {deiner-Lieblingssprache}, Teile 1-4" von Sedgewick.

61
autistic

TRIE:
Wir können ein Suchschema haben, bei dem statt eines Vergleichs eines gesamten Suchschlüssels mit allen vorhandenen Schlüsseln (z. B. einem Hash-Schema) auch jedes Zeichen des Suchschlüssels verglichen werden kann. Nach dieser Idee können wir eine Struktur erstellen (siehe Abbildung unten), die drei vorhandene Schlüssel enthält: "dad", "dab" und "cab".

         [root]
     ...// | \\...
           |  \
           c   d
           |    \
          [*]    [*]
      ...//|\.  ./|\\...        Fig-I
        a       a
       /       /
     [*]      [*]
 ...//|\..  ../|\\...
    /        /   \
   B        b     d
  /        /       \
 []       []       []

(cab)   (dab)     (dad)

Dies ist im Wesentlichen ein M-Baum mit einem internen Knoten, dargestellt als [*] und einem Blattknoten, dargestellt als []. Diese Struktur wird als triebezeichnet. Die Verzweigungsentscheidung an jedem Knoten kann gleich der Anzahl der eindeutigen Symbole des Alphabets, z. B. R, gehalten werden. Für englische Kleinbuchstaben az ist R = 26 ; für erweiterte ASCII Alphabete, R = 256 und für binäre Ziffern/Zeichenketten R = 2.

Compact TRIE:
Typischerweise verwendet ein Knoten in einem trieein Array mit der Größe = R und verschwendet somit Speicher, wenn jeder Knoten weniger Kanten hat Basierend auf diesen Variationen werden trieauch als "compact trie" und "compressed trie" bezeichnet. Eine konsistente Nomenklatur ist zwar selten, aber am häufigsten Die Version eines kompakten triewird gebildet, indem alle Kanten gruppiert werden, wenn Knoten eine einzelne Kante haben. Mit diesem Konzept wird die obige (Fig-I) triewith Die Tasten „Papa“, „Tupfer“ und „Fahrerhaus“ können die folgende Form annehmen.

         [root]
     ...// | \\...
           |  \
          cab  da
           |    \
          [ ]   [*]                Fig-II
               ./|\\...
                 |  \
                 b   d
                 |    \
                []    []

Beachten Sie, dass jedes von "c", "a" und "b" die einzige Kante für den entsprechenden übergeordneten Knoten ist und daher zu einer einzigen Kante "cab" zusammengefasst wird. In ähnlicher Weise werden "d" und "a" zu einer einzelnen Kante mit der Bezeichnung "da" zusammengeführt.

Radix Trie:
Der Ausdruck radixbedeutet in der Mathematik die Basis eines Zahlensystems und gibt im Wesentlichen die Anzahl der eindeutigen Symbole an, die für die Darstellung einer beliebigen Zahl in diesem System erforderlich sind. Das Dezimalsystem ist beispielsweise das Radix zehn , und das binäre System ist die Basis 2. Wenn wir nach dem ähnlichen Konzept eine Datenstruktur oder einen Algorithmus anhand der Anzahl der eindeutigen Symbole des zugrunde liegenden Darstellungssystems charakterisieren möchten, kennzeichnen wir das Konzept mit dem Begriff „Basis“ Beispiel: "Radix-Sortierung" für einen bestimmten Sortieralgorithmus. In derselben Logikzeile sind alle Varianten von trie, deren Eigenschaften (wie Tiefe, Speicherbedarf, Laufzeit für Suchfehler/Treffer usw.) ) abhängig vom Radix der zugrunde liegenden Alphabete, können wir sie Radix "trie's" nennen, zum Beispiel ein nicht komprimiertes sowie ein komprimiertes trie, wenn wir Alphabete az verwenden, können wir es nennen a radix 26 trie. Jeder Versuch, der nur zwei Symbole verwendet (traditionell '0' und '1'), kann als ar bezeichnet werden adix 2 trie. Irgendwie schränkten jedoch viele Literaturstellen die Verwendung des Begriffs „Radix Trie“ nur für die komprimierten trieein.

Vorspiel zu PATRICIA Tree/Trie:
Es wäre interessant zu bemerken, dass auch Zeichenfolgen als Schlüssel mit Binär-Alphabeten dargestellt werden können. Wenn wir ASCII Kodierung annehmen, kann ein Schlüssel "dad" in binärer Form geschrieben werden, indem die binäre Darstellung jedes Zeichens nacheinander geschrieben wird, beispielsweise als "110011100001)"11001 ”durch sequentielles Schreiben der binären Formen 'd', 'a' und 'd'. Mit diesem Konzept wird ein trie(mit Radix Two) Im Folgenden stellen wir dieses Konzept anhand einer vereinfachten Annahme dar, dass die Buchstaben "a", "b", "c" und "'d "aus einem kleineren Alphabet anstelle von ASCII stammen.

Anmerkung zu Abb. III: Um die Darstellung zu vereinfachen, nehmen wir, wie bereits erwähnt, ein Alphabet mit nur 4 Buchstaben {a, b, c, d} an. Die entsprechenden binären Darstellungen lauten "00", "01", "10" "Und" 11 ". Mit diesem Befehl werden unsere Saitentasten" dad "," dab "und" cab "zu" 110011 "," 110001 "und" 100001 " Fig-III (Bits werden von links nach rechts gelesen, genauso wie Zeichenketten von links nach rechts gelesen werden).

          [root]
             \1               
              \
              [*]
             0/ \1               
             /   \
           [*]   [*]         
           0/     /               
           /     /0
         [*]    [*]      
        0/      /               
        /      /0
      [*]    [*]
     0/     0/ \1                Fig-III
     /      /   \
    [*]   [*]   [*]
     \1     \1    \1
      \      \     \
      []     []    []
    (cab)   (dab) (dad)

PATRICIA Trie/Tree:
Wenn wir die obige Binärdatei trie(Fig-III) unter Verwendung der Einzelkanten-Komprimierung komprimieren, hätte sie viel weniger Knoten als oben gezeigt, und dennoch wären die Knoten immer noch mehr als die 3, die Anzahl der Schlüssel, die es enthält. Donald R. Morrisonfand (1968) einen innovativen Weg, um binär triezur Darstellung von N zu verwenden Schlüssel nur mit N Knoten und er nannte diese Datenstruktur PATRICIA . Seine Trie-Struktur hat im Wesentlichen einzelne Kanten beseitigt (Einwegverzweigung); und Dabei löste er auch die Vorstellung von zwei Arten von Knoten - inneren Knoten (die keinen Schlüssel darstellen) und Blattknoten (die Schlüssel darstellen). Anders als die oben erläuterte Verdichtungslogik verwendet sein Versuch ein anderes Konzept Dabei enthält jeder Knoten eine Angabe darüber, wie viele Bits eines Schlüssels für eine Verzweigungsentscheidung übersprungen werden müssen.Ein weiteres Merkmal seines PATRICIA-Versuchs besteht darin, dass die Schlüssel nicht gespeichert werden - was bedeutet, dass eine solche Datenstruktur nicht für eine Antwort geeignet ist Bei Fragen wie listet alle Schlüssel auf, die mit einem bestimmten Präfix übereinstimmen, eignet sich jedoch zum Auffinden von ob ein Schlüssel im trie vorhanden ist oder nicht). Trotzdem wurde der Begriff Patricia Tree oder Patricia Trie seitdem in vielen verschiedenen, aber ähnlichen Sinnen verwendet, um beispielsweise einen kompakten Trie [NIST] oder einen Radix-Trie mit Radix zwei [wie in einem subtilen] zu bezeichnen Weg in WIKI] und so weiter.

Trie, der möglicherweise kein Radix-Trie ist:
Ternary Search Trie(aka Ternary Search Tree) wird oft als TST abgekürzt und ist eine Datenstruktur (vorgeschlagen von J. Bentleyund R. Sedgewick), die einem Trie mit Dreifachverzweigung sehr ähnlich sehen. Für einen solchen Baum hat jeder Knoten ein charakteristisches Alphabet 'x', so dass die Verzweigungsentscheidung davon abhängt, ob a Zeichen eines Schlüssels ist kleiner als, gleich oder größer als "x". Aufgrund dieser festen 3-Wege-Verzweigungsfunktion bietet es eine speichereffiziente Alternative für trie, insbesondere wenn R (radix) sehr groß ist, wie zum Beispiel für Unicode Alphabete: Interessanterweise wird der TST im Gegensatz zu (R-Weg) trienicht durch R beeinflusst. Beispielsweise ist der Suchfehler nach TST ln (N)im Gegensatz zu logR(N)für R-Way Trie. Speicherbedarf von TST, im Gegensatz zu R-way trieist NICHT auch eine Funktion von R. Wir sollten also vorsichtig sein, aufzurufen a TST a radix-trie Ich persönlich denke nicht, dass wir es als radix-trie bezeichnen sollten, da (soweit ich weiß) keiner seiner Merkmale durch den Radix R seiner zugrunde liegenden Alphabete beeinflusst wird.

16
KGhatak