web-dev-qa-db-fra.com

Quelle est la différence entre les structures de données trie et radix trie?

Les structures de données trie et radix trie sont-elles la même chose?

S'ils sont identiques, alors quelle est la signification de radix trie (AKA Patricia trie)?

82
Daggerhunt

Un arbre Radix est une version compressée d'un trie. Dans un trie, sur chaque Edge, vous écrivez une seule lettre, tandis que dans un arbre PATRICIA (ou arbre radix) vous stockez des mots entiers.

Supposons maintenant que vous avez les mots hello, hat et have. Pour les stocker dans un trie, cela ressemblerait à:

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

Et vous avez besoin de neuf nœuds. J'ai placé les lettres dans les nœuds, mais en fait, elles étiquettent les bords.

Dans un arbre Radix, vous aurez:

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

et vous n'avez besoin que de cinq nœuds. Dans l'image ci-dessus, les nœuds sont les astérisques.

Donc, dans l'ensemble, un arbre Radix prend moins de mémoire, mais il est plus difficile à mettre en œuvre. Sinon, le cas d'utilisation des deux est à peu près le même.

105
Ivaylo Strandjev

Ma question est de savoir si Trie structure de données et Radix Trie sont la même chose?

Bref, non. La catégorie Radix Trie décrit une catégorie particulière de Trie, mais cela ne signifie pas que tous les essais sont des essais radix.

S'ils ne sont pas identiques, quelle est la signification de Radix trie (alias Patricia Trie)?

Je suppose que vous vouliez écrire pas dans votre question, d'où ma correction.

De même, PATRICIA dénote un type spécifique de radix trie, mais tous les essais radix ne sont pas des essais PATRICIA.


Qu'est-ce qu'un trie?

"Trie" décrit une structure de données arborescente pouvant être utilisée comme un tableau associatif, où les branches ou les arêtes correspondent à parties d'une clé. La définition de parties est plutôt vague ici, car différentes implémentations d'essais utilisent différentes longueurs de bits pour correspondre aux bords. Par exemple, un trie binaire a deux fronts par nœud qui correspondent à un 0 ou un 1, tandis qu'un trie à 16 voies a seize fronts par nœud qui correspondent à quatre bits (ou à un chiffre hexidécimal: 0x0 à 0xf).

Ce diagramme, extrait de Wikipédia, semble représenter un trie avec (au moins) les touches 'A', 'à', 'thé', 'ted', 'ten' et 'inn' insérées:

Basic trie

Si ce trie devait stocker des éléments pour les clés 't', 'te', 'i' ou 'in', il devrait y avoir des informations supplémentaires présentes à chaque nœud pour distinguer les nœuds nulles et les nœuds avec des valeurs réelles.


Qu'est-ce qu'un radix trie?

"Radix trie" semble décrire une forme de trie qui condense les parties de préfixe communes, comme Ivaylo Strandjev l'a décrit dans sa réponse. Considérez qu'un trie à 256 voies qui indexe les touches "sourire", "sourire", "sourire" et "sourire" en utilisant les affectations statiques suivantes:

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;

Chaque indice accède à un nœud interne. Cela signifie récupérer smile_item, vous devez accéder à sept nœuds. Huit accès aux nœuds correspondent à smiled_item et smiles_item et neuf à smiling_item. Pour ces quatre éléments, il y a au total quatorze nœuds. Cependant, ils ont tous en commun les quatre premiers octets (correspondant aux quatre premiers nœuds). En condensant ces quatre octets pour créer un root qui correspond à ['s']['m']['i']['l'], quatre accès aux nœuds ont été optimisés. Cela signifie moins de mémoire et moins d'accès aux nœuds, ce qui est une très bonne indication. L'optimisation peut être appliquée récursivement pour réduire le besoin d'accéder à des octets de suffixe inutiles. Finalement, vous arrivez à un point où vous ne comparez que différences entre la clé de recherche et les clés indexées aux emplacements indexés par le trie. Ceci est un radix trie.

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

Pour récupérer des éléments, chaque nœud a besoin d'une position. Avec une clé de recherche de "sourires" et un root.position sur 4, on accède à root["smiles"[4]], qui se trouve être root['e']. Nous le stockons dans une variable appelée current. current.position est 5, qui est l'emplacement de la différence entre "smiled" et "smiles", donc le prochain accès sera root["smiles"[5]]. Cela nous amène à smiles_item, et la fin de notre chaîne. Notre recherche est terminée et l'élément a été récupéré, avec seulement trois accès au nœud au lieu de huit.


Qu'est-ce qu'un trie PATRICIA?

Un trie PATRICIA est une variante des essais radix pour lesquels il ne devrait y avoir que des nœuds n utilisés pour contenir des éléments n. Dans notre pseudocode de tri de radix grossièrement démontré ci-dessus, il y a cinq nœuds au total: root (qui est un nœud nul; il ne contient aucune valeur réelle), root['e'], root['e']['d'], root['e']['s'] et root['i']. Dans un trio PATRICIA, il ne devrait y en avoir que quatre. Voyons comment ces préfixes peuvent différer en les regardant en binaire, car PATRICIA est un algorithme binaire.

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

Considérons que les nœuds sont ajoutés dans l'ordre où ils sont présentés ci-dessus. smile_item est la racine de cet arbre. La différence, en gras pour la rendre un peu plus facile à repérer, se trouve dans le dernier octet de "smile", au bit 36. Jusqu'à ce point, tous nos nœuds ont le préfixe le même. smiled_node appartient à smile_node[0]. La différence entre "smiled" et "smiles" se produit au bit 43, où "smiles" a un bit "1", donc smiled_node[1] est smiles_node.

Plutôt que d'utiliser NULL comme branches et/ou informations internes supplémentaires pour indiquer quand une recherche se termine, les branches renvoient p quelque part dans l'arborescence, donc une recherche se termine lorsque le décalage à tester - diminue plutôt que d'augmenter. Voici un schéma simple d'un tel arbre (bien que PATRICIA soit vraiment plus un graphique cyclique, qu'un arbre, comme vous le verrez), qui a été inclus dans le livre de Sedgewick mentionné ci-dessous:

Simple PATRICIA diagram

Un algorithme PATRICIA plus complexe impliquant des clés de longueur variable est possible, bien que certaines des propriétés techniques de PATRICIA soient perdues au cours du processus (à savoir que tout nœud contient un préfixe commun avec le nœud précédent):

Complex PATRICIA diagram

En se ramifiant comme ceci, il y a un certain nombre d'avantages: Chaque nœud contient une valeur. Cela inclut la racine. En conséquence, la longueur et la complexité du code deviennent beaucoup plus courtes et probablement un peu plus rapides en réalité. Au moins une branche et au plus k branches (où k est le nombre de bits dans la clé de recherche) sont suivies pour localiser un élément. Les nœuds sont minuscules, car ils ne stockent que deux branches chacun, ce qui les rend assez adaptés à l'optimisation de la localisation du cache. Ces propriétés font de PATRICIA mon algorithme préféré jusqu'à présent ...

Je vais abréger cette description ici, afin de réduire la gravité de mon arthrite imminente, mais si vous voulez en savoir plus sur PATRICIA, vous pouvez consulter des livres tels que "The Art of Computer Programming, Volume 3" de Donald Knuth , ou l'un des "Algorithmes dans {votre-langue-préférée}, parties 1-4" de Sedgewick.

61
autistic

TRIE:
Nous pouvons avoir un schéma de recherche où au lieu de comparer une clé de recherche entière avec toutes les clés existantes (comme un schéma de hachage), nous pourrions également comparer chaque caractère de la clé de recherche. Suivant cette idée, nous pouvons construire une structure (comme indiqué ci-dessous) qui a trois clés existantes - " papa", " dab", et "cab ”.

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

(cab)   (dab)     (dad)

Il s'agit essentiellement d'un arbre M-aire avec un nœud interne, représenté par [*] et un nœud feuille, représenté par []. Cette structure est appelée a trie. La décision de branchement à chaque nœud peut être maintenue égale au nombre de symboles uniques de l'alphabet, disons R. Pour les alphabets anglais minuscules a-z, R = 26; pour les alphabets étendus ASCII, R = 256 et pour les chiffres/chaînes binaires R = 2.

Compact TRIE:
En règle générale, un nœud dans un trie utilise un tableau de taille = R et entraîne donc un gaspillage de mémoire lorsque chaque nœud a moins de bords. Pour contourner le problème de mémoire, diverses propositions ont été faites. Sur la base de ces variations trie sont également nommées " compact trie" et " trie compressé = ". Bien qu'une nomenclature cohérente soit rare, une version la plus courante d'un compact trie est formée en regroupant tous les bords lorsque les nœuds ont un seul bord. En utilisant ce concept, les éléments ci-dessus (Fig-I) trie avec les touches "papa", "dab" et "cab" peuvent prendre la forme ci-dessous.

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

Notez que chacun des "c", "a" et "b" est le seul Edge pour son nœud parent correspondant et, par conséquent, ils sont agglomérés en un seul "cab" Edge. De même, "d" et a "sont fusionnés en un seul bord intitulé" da ".

Radix Trie:
Le terme radix, en mathématiques, signifie une base d'un système numérique, et il indique essentiellement le nombre de symboles uniques nécessaires pour représenter n'importe quel nombre dans ce système. Par exemple, le système décimal est radix dix et le système binaire est radix deux. En utilisant le concept similaire, lorsque nous souhaitons caractériser une structure de données ou un algorithme par le nombre de symboles uniques du système de représentation sous-jacent, nous étiquetons le concept avec le terme "radix". Par exemple, "tri radix" pour certains algorithmes de tri. Dans la même logique, toutes les variantes de trie dont les caractéristiques (telles que la profondeur, le besoin en mémoire, la recherche de miss/run run, etc.) dépendent de la base de les alphabets sous-jacents, nous pouvons les appeler radix "trie". Par exemple, un non compacté ainsi qu'un compact trie lorsque utilise les alphabets az, nous pouvons l'appeler un radix 26 trie. Tout trie qui n'utilise que deux symboles (traditionnellement "0" et "1") peut être appelé un radix 2 trie. Cependant, en quelque sorte, de nombreuses littératures ont restreint l'utilisation du terme "Radix Trie" uniquement pour le trie compacté.

Prélude à PATRICIA Tree/Trie:
Il serait intéressant de remarquer que même les chaînes comme clés peuvent être représentées en utilisant des alphabets binaires. Si nous supposons ASCII, alors une clé "papa" peut être écrite sous forme binaire en écrivant la représentation binaire de chaque caractère dans l'ordre, par exemple "11001= 0110000111001 ”en écrivant les formes binaires de 'd', 'a' et 'd' séquentiellement. En utilisant ce concept, un trie (avec Radix Two) peut être formé. Ci-dessous, nous décrivons ce concept en utilisant une hypothèse simplifiée selon laquelle les lettres "a", "b", "c" et "d" proviennent d’un alphabet plus petit au lieu de ASCII.

Note pour la Fig-III: Comme mentionné, pour rendre la représentation facile, supposons un alphabet avec seulement 4 lettres {a, b, c, d} et leurs représentations binaires correspondantes sont "00", "01", "10" et "11" respectivement. Avec cela, nos clés de chaîne "papa", "dab" et "cab" deviennent respectivement "110011", "110001" et "100001". Le tri pour cela sera comme indiqué ci-dessous dans la Fig-III (les bits sont lus de gauche à droite tout comme les chaînes sont lues de gauche à droite).

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

PATRICIA Trie/Arbre:
Si nous compactons le binaire ci-dessus trie (Fig-III) en utilisant un compactage Edge unique, il aurait beaucoup moins de nœuds que celui montré ci-dessus et pourtant, les nœuds seraient toujours supérieurs aux 3, le nombre de clés qu'il contient. Donald R. Morrison a trouvé (en 1968) une manière innovante d'utiliser le binaire trie pour représenter N clés utilisant uniquement N nœuds et il a nommé cette structure de données PATRICIA . Sa structure de trie s'est essentiellement débarrassée des bords simples (ramification unidirectionnelle); et ce faisant, il s'est également débarrassé de la notion de deux types de nœuds - les nœuds intérieurs (qui ne représentent aucune clé) et les nœuds feuilles (qui représentent les clés). Contrairement à la logique de compactage expliquée ci-dessus, son trie utilise un concept différent où chaque nœud comprend une indication du nombre de bits d'une clé à ignorer pour prendre une décision de branchement. Encore une autre caractéristique de son trio PATRICIA est qu'il ne stocke pas les clés - ce qui signifie qu'une telle structure de données ne sera pas adaptée pour répondre à des questions comme, lister toutes les clés qui correspondent à un préfixe donné, mais est bon pour trouver si une clé existe ou non dans le trie. Néanmoins, le terme Patricia Tree ou Patricia Trie a, depuis lors, été utilisé dans de nombreux sens différents mais similaires, tels que, pour indiquer un trie compact [NIST], ou pour indiquer un radix trie avec radix deux [comme indiqué dans un subtil façon dans WIKI] et ainsi de suite.

Trie qui n'est peut-être pas un Radix Trie:
Ternary Search Trie (alias Ternary Search Tree) souvent abrégé en TST est une structure de données (proposée par J. Bentley et R. Sedgewick) qui ressemble beaucoup à un trie avec ramification à trois voies. Pour un tel arbre, chaque nœud a un alphabet caractéristique "x" de sorte que la décision de branchement est déterminée par le caractère inférieur, égal ou supérieur à "x" d’un caractère d’une clé. En raison de cette fonction de branchement à 3 voies fixe, il fournit une alternative efficace en mémoire pour le trie, en particulier lorsque R (radix) est très grand, comme pour les alphabets Unicode. Fait intéressant, le TST, contrairement à (R-way) trie, n'a pas ses caractéristiques influencées par R. Par exemple, la recherche de TST est ln (N) par opposition logR(N) pour T-way R-way. Les besoins en mémoire de TST, contrairement à R-way trie is [~ # ~] not [~ # ~] a function of R aussi. Nous devons donc être prudents d'appeler un TST un radix-trie. Personnellement, je ne pense pas que nous devrions l'appeler un radix-trie car aucune (à ma connaissance) de ses caractéristiques n'est influencée par la radix, R, de ses alphabets sous-jacents.

16
KGhatak