Qual è la differenza tra le strutture dati trie e radix trie?


95

Le strutture dati trie e radix trie sono la stessa cosa?

Se sono uguali, qual è il significato di radix trie (AKA Patricia trie)?


4
Sono l'unico che trova un po 'fastidioso che il tag sia radix-treepiuttosto che radix-trie? Inoltre, ci sono alcune domande contrassegnate con esso.
errantlinguist

@errantlinguist Wikipedia intitola l' radix triearticolo come Radix tree. Inoltre, il termine "albero Radix" è ampiamente utilizzato in letteratura. Se qualcosa che chiamasse "prefissi alberi" avrebbe più senso per me. Dopotutto, sono tutte strutture di dati ad albero .
Amelio Vazquez-Reina il

Inoltre: "Qual è il significato di radix trie (AKA Patricia trie)?" questo presuppone che gli alberi radix e gli alberi PATRICIA siano la stessa cosa, ma non lo sono (ad esempio, vedere questa risposta ). Gli alberi PATRICIA sono alberi che ottieni eseguendo l' algoritmo PATRICIA (anche FYI PATRICIA è un acronimo, che sta per "Practical Algorithm To Retrieve Information Coded in Alphanumeric"). Gli alberi risultanti possono essere intesi come alberi radice con radix = 2, il che significa che si attraversa l'albero cercando log2(radix)=1bit della stringa di input alla volta.
Amelio Vazquez-Reina il

Risposte:


118

Un albero radice è una versione compressa di un trie. In un trie, su ogni bordo scrivi una singola lettera, mentre in un albero PATRICIA (o albero radice) memorizzi parole intere.

Ora, supponi di avere le parole hello, hate have. Per memorizzarli in un trie , sarebbe simile:

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

E hai bisogno di nove nodi. Ho posizionato le lettere nei nodi, ma in realtà etichettano i bordi.

In un albero radix, avrai:

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

e hai bisogno solo di cinque nodi. Nell'immagine sopra i nodi sono gli asterischi.

Quindi, nel complesso, un albero radix richiede meno memoria , ma è più difficile da implementare. Altrimenti il ​​caso d'uso di entrambi è praticamente lo stesso.


Grazie ... Puoi fornirmi una buona risorsa per studiare il trie DS ... Sarebbe di grande aiuto ...
Aryak Sengupta

Credo che l'unica cosa che ho usato quando ho implementato Trie per la prima volta fosse l' articolo di wikipedia . Non sto dicendo che sia perfetto, ma è abbastanza buono.
Ivaylo Strandjev

1
posso dire che la ricerca in TRIE è più veloce di Radix tree? Perché in TRIE se vuoi cercare il carattere successivo devi vedere l'i esimo indice nell'array figlio del nodo corrente ma nell'albero radix devi cercare tutti i nodi figli in sequenza. Vedi il codice di
Prova il

4
In realtà in un albero radice non puoi avere più di un bordo che inizia con la stessa lettera, quindi puoi usare la stessa indicizzazione costante.
Ivaylo Strandjev

1
@ Trying Algorithmically Radix è più veloce di TRIE, ecco perché vale la pena fare la compressione. Meno nodi da caricare e meno spazio sono generalmente migliori. Detto questo, la qualità dell'implementazione può variare.
Glenn Teitelbaum

68

La mia domanda è se la struttura dei dati di Trie e Radix Trie siano la stessa cosa?

In breve, no. La categoria Radix Trie descrive una particolare categoria di Trie , ma ciò non significa che tutti i tentativi siano radix.

Se non sono uguali, qual è il significato di Radix trie (aka Patricia Trie)?

Presumo che volevi scrivere non sono nella tua domanda, da qui la mia correzione.

Allo stesso modo, PATRICIA denota un tipo specifico di radix trie, ma non tutti i radix try sono PATRICIA.


Cos'è un trie?

"Trie" descrive una struttura di dati ad albero adatta per l'uso come array associativo, dove rami o bordi corrispondono a parti di una chiave. La definizione di parti è piuttosto vaga, qui, perché diverse implementazioni di try utilizzano diverse lunghezze di bit per corrispondere ai bordi. Ad esempio, un trie binario ha due lati per nodo che corrispondono a uno 0 o un 1, mentre un trie a 16 vie ha sedici lati per nodo che corrispondono a quattro bit (o una cifra esadecimale: da 0x0 a 0xf).

Questo diagramma, recuperato da Wikipedia, sembra rappresentare un trie con (almeno) le chiavi "A", "to", "tea", "ted", "ten" e "inn" inserite:

Trie di base

Se questo trie dovesse memorizzare elementi per le chiavi "t", "te", "i" o "in", ci sarebbe bisogno di informazioni aggiuntive presenti in ogni nodo per distinguere tra nodi nulli e nodi con valori effettivi.


Cos'è un radix trie?

"Radix trie" sembra descrivere una forma di trie che condensa parti di prefisso comuni, come Ivaylo Strandjev ha descritto nella sua risposta. Considera che un trie a 256 vie che indicizza i tasti "smile", "smiled", "smiles" e "smiling" utilizzando le seguenti assegnazioni statiche:

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;

Ogni pedice accede a un nodo interno. Ciò significa che per recuperare smile_item, devi accedere a sette nodi. Otto accessi al nodo corrispondono a smiled_iteme smiles_iteme nove a smiling_item. Per questi quattro elementi, ci sono quattordici nodi in totale. Tuttavia, hanno tutti i primi quattro byte (corrispondenti ai primi quattro nodi) in comune. Condensando quei quattro byte per creare un rootche corrisponde a ['s']['m']['i']['l'], sono stati ottimizzati quattro accessi ai nodi. Ciò significa meno memoria e meno accessi ai nodi, il che è un'ottima indicazione. L'ottimizzazione può essere applicata in modo ricorsivo per ridurre la necessità di accedere a byte di suffisso non necessari. Alla fine, arrivi a un punto in cui stai solo confrontando le differenze tra la chiave di ricerca e le chiavi indicizzate nelle posizioni indicizzate dal trie. Questo è un trie radix.

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

Per recuperare gli elementi, ogni nodo necessita di una posizione. Con una chiave di ricerca di "sorrisi" e una root.positiondi 4, accediamo root["smiles"[4]], che sembra essere root['e']. Lo memorizziamo in una variabile chiamata current. current.positionè 5, che è la posizione della differenza tra "smiled"e "smiles", quindi il prossimo accesso sarà root["smiles"[5]]. Questo ci porta a smiles_item, e alla fine della nostra stringa. La nostra ricerca è terminata e l'elemento è stato recuperato, con solo tre accessi al nodo invece di otto.


Cos'è un trie PATRICIA?

Un trie PATRICIA è una variante dei tentativi radix per i quali dovrebbero esserci solo nnodi usati per contenere noggetti. Nel nostro grossolanamente dimostrato radice trie pseudocodice sopra, ci sono cinque nodi complessivamente: root(che è un nodo nullaria, contiene alcun valore effettivo), root['e'], root['e']['d'], root['e']['s']e root['i']. In un trie PATRICIA ce ne dovrebbero essere solo quattro. Diamo un'occhiata a come questi prefissi potrebbero differire guardandoli in binario, poiché PATRICIA è un algoritmo binario.

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

Consideriamo che i nodi vengono aggiunti nell'ordine in cui sono presentati sopra. smile_itemè la radice di questo albero. La differenza, evidenziata in grassetto per renderla leggermente più facile da individuare, è nell'ultimo byte di "smile", al bit 36. Fino a questo punto, tutti i nostri nodi hanno lo stesso prefisso. smiled_nodeappartiene a smile_node[0]. La differenza tra "smiled"e si "smiles"verifica al bit 43, dove "smiles"ha un bit "1", così smiled_node[1]è smiles_node.

Invece di utilizzare NULLcome rami e / o informazioni interne supplementari per indicare quando una ricerca termina, i rami collegano indietro fino al qualche albero, quindi una ricerca termina quando l'offset di prova diminuisce invece di aumentare. Ecco un semplice diagramma di un tale albero (sebbene PATRICIA sia davvero più un grafico ciclico, che un albero, come vedrai), che è stato incluso nel libro di Sedgewick menzionato di seguito:

Schema PATRICIA semplice

È possibile un algoritmo PATRICIA più complesso che coinvolge chiavi di lunghezza variante, sebbene alcune delle proprietà tecniche di PATRICIA vengano perse nel processo (vale a dire che ogni nodo contiene un prefisso comune con il nodo precedente):

Schema PATRICIA complesso

Diramando in questo modo, ci sono una serie di vantaggi: Ogni nodo contiene un valore. Ciò include la radice. Di conseguenza, la lunghezza e la complessità del codice diventano molto più brevi e probabilmente un po 'più veloci nella realtà. Almeno un ramo e al massimo krami (dove kè il numero di bit nella chiave di ricerca) vengono seguiti per individuare un elemento. I nodi sono piccoli , perché memorizzano solo due rami ciascuno, il che li rende abbastanza adatti per l'ottimizzazione della località della cache. Queste proprietà fanno di PATRICIA il mio algoritmo preferito finora ...

Taglierò questa descrizione qui breve, al fine di ridurre la gravità della mia imminente artrite, ma se vuoi saperne di più su PATRICIA puoi consultare libri come "The Art of Computer Programming, Volume 3" di Donald Knuth , o uno qualsiasi degli "Algoritmi in {la tua lingua preferita}, parti 1-4" di Sedgewick.


Mi aiuterebbe a capire il significato del termine "Radix"! Capisco come, in modo naturale, possiamo provare a trasformare un TRIE in un TRIE compatto consentendo a più simboli / bordi di fondersi in un unico bordo. Tuttavia, non sono in grado di discernere perché un TRIE non compresso (semplicemente un TRIE) non possa essere definito come Radix TRIE.
KGhatak

@ Seb - Apprezzerei molto il tuo feedback sul post stackoverflow.com/questions/40087385/… su Radix Tree. Grazie in adv.
KGhatak

@BuckCherry Mi piacerebbe poterlo fare, ma tieni presente che poiché il mio computer è stato rubato non sarei in grado di impegnarmi per una risposta adeguata.
autistico

18

TRIE:
Possiamo avere uno schema di ricerca in cui invece di confrontare un'intera chiave di ricerca con tutte le chiavi esistenti (come uno schema hash), potremmo anche confrontare ogni carattere della chiave di ricerca. Seguendo questa idea, possiamo costruire una struttura (come mostrato di seguito) che abbia tre chiavi esistenti: " dad ", " dab " e " cab ".

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

(cab)   (dab)     (dad)

Questo è essenzialmente un albero M-ary con nodo interno, rappresentato come [*] e nodo foglia, rappresentato come []. Questa struttura è chiamata trie . La decisione di ramificazione in ogni nodo può essere mantenuta uguale al numero di simboli univoci dell'alfabeto, ad esempio R. Per gli alfabeti inglesi minuscoli az, R = 26; per alfabeti ASCII estesi, R = 256 e per cifre / stringhe binarie R = 2.

TRIE compatto: in
genere, un nodo in un trie utilizza un array con size = R e quindi causa uno spreco di memoria quando ogni nodo ha meno bordi. Per aggirare la preoccupazione della memoria, sono state avanzate varie proposte. Sulla base di queste variazioni i trie sono anche denominati " trie compatto " e " trie compresso ". Mentre una nomenclatura coerente è rara, una versione più comune di un trie compatto è formata raggruppando tutti i bordi quando i nodi hanno un unico bordo. Usando questo concetto, il trie sopra (Fig-I) con i tasti "dad", "dab" e "cab" può assumere la forma sottostante.

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

Si noti che ciascuno di "c", "a" e "b" è l'unico bordo per il nodo padre corrispondente e pertanto sono conglomerati in un unico bordo "cabina". Allo stesso modo, "d" e a "vengono uniti in un unico bordo etichettato come" da ".

Radix Trie:
Il termine radix , in matematica, significa una base di un sistema numerico, e indica essenzialmente il numero di simboli univoci necessari per rappresentare qualsiasi numero in quel sistema. Ad esempio, il sistema decimale è la radice dieci e il sistema binario è la radice due. Usando il concetto simile, quando siamo interessati a caratterizzare una struttura dati o un algoritmo dal numero di simboli univoci del sistema rappresentativo sottostante, etichettiamo il concetto con il termine "radix". Ad esempio, "radix sort" per determinati algoritmi di ordinamento. Nella stessa linea di logica, tutte le varianti di triele cui caratteristiche (come la profondità, il bisogno di memoria, il tempo di ricerca mancata / hit, ecc.) dipendono dalla radice degli alfabeti sottostanti, possiamo chiamarli radix "trie's". Ad esempio, un trie non compattato così come un trie compresso quando usa gli alfabeti az, possiamo chiamarlo trie radice 26 . Qualsiasi trie che utilizza solo due simboli (tradizionalmente "0" e "1") può essere chiamato trie radice 2 . Tuttavia, in qualche modo molte letterature hanno limitato l'uso del termine "Radix Trie" solo per il trie compatto .

Preludio a PATRICIA Tree / Trie:
Sarebbe interessante notare che anche le stringhe come chiavi possono essere rappresentate usando alfabeti binari. Se assumiamo la codifica ASCII, allora una chiave "papà" può essere scritta in forma binaria scrivendo la rappresentazione binaria di ogni carattere in sequenza, ad esempio " 01100100 01100001 01100100 " scrivendo forme binarie di "d", "a" e 'd' in sequenza. Usando questo concetto, si può formare un trie (con Radix Two). Di seguito descriviamo questo concetto utilizzando un presupposto semplificato che le lettere "a", "b", "c" e'd "provengano da un alfabeto più piccolo anziché ASCII.

Nota per la Fig-III: come accennato, per semplificare la rappresentazione, supponiamo che un alfabeto con solo 4 lettere {a, b, c, d} e le loro corrispondenti rappresentazioni binarie siano "00", "01", "10" e "11" rispettivamente. Con questo, le nostre chiavi di stringa "dad", "dab" e "cab" diventano rispettivamente "110011", "110001" e "100001". Il trie per questo sarà come mostrato di seguito in Fig-III (i bit vengono letti da sinistra a destra proprio come le stringhe vengono lette da sinistra a destra).

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

PATRICIA Trie / Tree:
Se compattiamo il trie binario sopra (Fig-III) usando la compattazione a bordo singolo, avrebbe molti meno nodi di quelli mostrati sopra e tuttavia, i nodi sarebbero ancora più di 3, il numero di chiavi che contiene . Donald R. Morrison trovò (nel 1968) un modo innovativo di usare il trie binario per rappresentare N chiavi usando solo N nodi e chiamò questa struttura dati PATRICIA. La sua struttura trie essenzialmente si è sbarazzata dei bordi singoli (ramificazione unidirezionale); e così facendo, si è anche sbarazzato della nozione di due tipi di nodi: nodi interni (che non rappresentano alcuna chiave) e nodi foglia (che raffigurano chiavi). A differenza della logica di compattazione spiegata sopra, il suo trie utilizza un concetto diverso in cui ogni nodo include un'indicazione di quanti bit di una chiave devono essere ignorati per prendere una decisione di ramificazione. Un'altra caratteristica del suo trie PATRICIA è che non memorizza le chiavi, il che significa che tale struttura di dati non sarà adatta per rispondere a domande come, elenca tutte le chiavi che corrispondono a un dato prefisso , ma è utile per scoprire se una chiave esiste o non nel trie. Tuttavia, il termine Patricia Tree o Patricia Trie è stato, da allora, usato in molti sensi diversi ma simili, come ad esempio, per indicare un trie compatto [NIST], o per indicare un trie radice con radice due [come indicato in un sottile modo in WIKI] e così via.

Trie che potrebbe non essere un Radix Trie:
Ternary Search Trie (aka Ternary Search Tree) spesso abbreviato come TST è una struttura dati (proposta da J. Bentley e R. Sedgewick ) che sembra molto simile a un trie con ramificazione a tre vie. Per tale albero, ogni nodo ha un alfabeto caratteristico "x" in modo che la decisione di ramificazione sia determinata dal fatto che un carattere di una chiave sia minore, uguale o maggiore di "x". A causa di questa caratteristica di ramificazione a 3 vie fissa, fornisce un'alternativa efficiente in termini di memoria per trie, specialmente quando R (radix) è molto grande come per gli alfabeti Unicode. È interessante notare che il TST, a differenza del trie (R-way) , non ha le sue caratteristiche influenzate da R. Ad esempio, la ricerca mancata per TST è ln (N)come opposto log R (N) per R-way Trie. Requisiti di memoria di TST, a differenza di R-way trie è non una funzione di R pure. Quindi dovremmo stare attenti a chiamare un TST un radix-trie. Personalmente, non credo che dovremmo chiamarlo radix-trie poiché nessuna delle sue caratteristiche (per quanto ne so) è influenzata dalla radice, R, dei suoi alfabeti sottostanti.


2
Come qualcuno che ha implementato PATRICIA secondo Morrison, Sedgewick e Knuth, posso dirti che l'algoritmo che hai descritto qui (che ho anche tentato di descrivere nella mia risposta) è ancora molto adatto per rispondere a domande come elencare tutte le chiavi che corrispondono a un dato prefisso . PS Bello vedere qualcun altro sulla palla ri: quell'altra domanda :) Mi piace questa spiegazione.
autistico

Re "non sarà adatto a rispondere a domande come, elenca tutte le chiavi che corrispondono a un dato prefisso", sul serio?
Pacerier

@Pacerier Sure! La PATRICIA classica memorizza un numero intero, che puoi utilizzare come indice per un array. Nell'array inserisci la stringa. Nel trie inserisci l'indice dell'array basato su 0 per la stringa. Fai in modo che le funzioni di ricerca e confronto ed estrazione bit operino sulla stringa corrispondente al numero intero piuttosto che all'intero, e se la tua funzione di inserimento è basata sulle altre (come dovrebbe essere, dato che c'è molta logica ripetuta lì) e tu ' sarai sulla buona strada. Potresti anche usarlo uintptr_tcome numero intero , dal momento che quel tipo sembra essere in genere previsto (sebbene non richiesto) esista.
autistico

Lei afferma che "molte letterature hanno limitato l'uso del termine" Radix Trie "solo per il trie compatto.". In realtà, non riesco a trovare nessun altro riferimento oltre a wikipedia. Hai trovato altri?
wds

@ wds - Potresti avere ragione, dato che non ricordo bene quali siano le risorse a cui ho fatto riferimento quando ho scritto questo. Una rapida ricerca su Google mi fornisce link come mathcs.emory.edu/~cheung/Courses/323/Syllabus/Text/trie02.html o tutorialsdiary.com/radix-trie-patricia-trie-or-compressed-trie che essenzialmente puntano a o (molto probabilmente) derivato / influenzato da wiki. Se trovo qualsiasi altra risorsa affidabile / accademica, pubblicherò qui.
KGhatak
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.