¿Cuál es la diferencia entre las estructuras de datos trie y radix trie?

96

¿Son las estructuras de datos trie y radix trie lo mismo?

Si no son iguales, ¿cuál es el significado de radix trie (también conocido como Patricia trie)?

Aryak Sengupta
fuente
4
¿Soy el único al que le resulta un poco molesto que la etiqueta sea en radix-treelugar de radix-trie? Además, hay bastantes preguntas etiquetadas con él.
errantlinguist
1
@errantlinguist Wikipedia titula el radix trieartículo como Radix tree. Además, el término "árbol Radix" se utiliza ampliamente en la literatura. Si algo llamaba a tries "árboles de prefijos" tendría más sentido para mí. Después de todo, todas son estructuras de datos de árbol .
Amelio Vazquez-Reina
También: "¿Cuál es el significado de radix trie (también conocido como Patricia trie)?" esto asume que los árboles de radix y los árboles de PATRICIA son una y la misma cosa, pero no lo son (por ejemplo, vea esta respuesta ). Los árboles PATRICIA son árboles que se obtienen al ejecutar el algoritmo PATRICIA (también FYI PATRICIA es un acrónimo, que significa "Algoritmo práctico para recuperar información codificada en alfanumérico"). Los árboles resultantes pueden entenderse como árboles de base con radix = 2, lo que significa que atraviesa el árbol buscando log2(radix)=1bits de la cadena de entrada a la vez.
Amelio Vazquez-Reina

Respuestas:

119

Un árbol de radix es una versión comprimida de un trie. En un trie, en cada borde escribe una sola letra, mientras que en un árbol PATRICIA (o árbol de base) almacena palabras completas.

Ahora, supongamos que tiene las palabras hello, haty have. Para almacenarlos en un trie , se vería así:

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

Y necesitas nueve nodos. He colocado las letras en los nodos, pero de hecho etiquetan los bordes.

En un árbol de radix, tendrás:

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

y solo necesitas cinco nodos. En la imagen de arriba, los nodos son los asteriscos.

Entonces, en general, un árbol de base requiere menos memoria , pero es más difícil de implementar. De lo contrario, el caso de uso de ambos es prácticamente el mismo.

Ivaylo Strandjev
fuente
Gracias ... ¿Puede proporcionarme un buen recurso para estudiar trie DS ... Eso sería de gran ayuda ...
Aryak Sengupta
Creo que lo único que usé cuando implementé Trie por primera vez fue el artículo de wikipedia . No digo que sea perfecto, pero es lo suficientemente bueno.
Ivaylo Strandjev
1
¿Puedo decir que buscar en TRIE es más rápido que Radix Tree? Porque en TRIE, si desea buscar el siguiente carácter, debe ver el índice i en la matriz secundaria del nodo actual, pero en el árbol de base necesita buscar todos los nodos secundarios secuencialmente. Consulte el código de
Probar el
4
En realidad, en un árbol de base no puede tener más de un borde que comience con la misma letra, por lo que puede usar la misma indexación constante.
Ivaylo Strandjev
1
@Probar algorítmicamente Radix es más rápido que TRIE, por eso vale la pena hacer la compresión. Menos nodos para cargar y menos espacio son generalmente mejores. Dicho esto, la calidad de la implementación puede variar.
Glenn Teitelbaum
68

Mi pregunta es si la estructura de datos de Trie y Radix Trie son la misma cosa.

En resumen, no. La categoría Radix Trie describe una categoría particular de Trie , pero eso no significa que todos los intentos sean radix.

Si no son iguales, ¿cuál es el significado de Radix trie (también conocida como Patricia Trie)?

Supongo que tu intención de escribir no está en tu pregunta, de ahí mi corrección.

De manera similar, PATRICIA denota un tipo específico de intento de base, pero no todos los intentos de base son intentos de PATRICIA.


¿Qué es un trie?

"Trie" describe una estructura de datos de árbol adecuada para su uso como una matriz asociativa, donde las ramas o los bordes corresponden a partes de una clave. La definición de partes es bastante vaga, aquí, porque diferentes implementaciones de intentos usan diferentes longitudes de bits para corresponder a los bordes. Por ejemplo, un trie binario tiene dos bordes por nodo que corresponden a un 0 o un 1, mientras que un trie de 16 vías tiene dieciséis bordes por nodo que corresponden a cuatro bits (o un dígito hexadecimal: 0x0 hasta 0xf).

Este diagrama, obtenido de Wikipedia, parece representar un trie con (al menos) las claves 'A', 'a', 'té', 'ted', 'diez' e 'posada' insertadas:

Trie básico

Si este intento fuera a almacenar elementos para las claves 't', 'te', 'i' o 'in', se necesitaría información adicional presente en cada nodo para distinguir entre nodos nulares y nodos con valores reales.


¿Qué es un radix trie?

"Radix trie" parece describir una forma de trie que condensa partes de prefijos comunes, como Ivaylo Strandjev describió en su respuesta. Considere que un trie de 256 vías que indexa las teclas "sonríe", "sonrió", "sonríe" y "sonríe" usando las siguientes asignaciones estáticas:

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;

Cada subíndice accede a un nodo interno. Eso significa que para recuperar smile_item, debe acceder a siete nodos. Ocho accesos a nodos corresponden a smiled_itemy smiles_item, y nueve a smiling_item. Para estos cuatro elementos, hay catorce nodos en total. Sin embargo, todos tienen los primeros cuatro bytes (correspondientes a los primeros cuatro nodos) en común. Al condensar esos cuatro bytes para crear un rootque corresponda ['s']['m']['i']['l'], se han optimizado los accesos de cuatro nodos. Eso significa menos memoria y menos accesos a los nodos, lo cual es una muy buena indicación. La optimización se puede aplicar de forma recursiva para reducir la necesidad de acceder a bytes de sufijo innecesarios. Eventualmente, llega a un punto en el que solo está comparando las diferencias entre la clave de búsqueda y las claves indexadas en ubicaciones indexadas por el trie.. Este es un trie radical.

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

Para recuperar elementos, cada nodo necesita una posición. Con una clave de búsqueda de "sonrisas" y una root.positionde 4, accedemos root["smiles"[4]], que resulta ser root['e']. Almacenamos esto en una variable llamada current. current.positiones 5, que es la ubicación de la diferencia entre "smiled"y "smiles", por lo que el próximo acceso será root["smiles"[5]]. Esto nos lleva al smiles_itemfinal de nuestra cadena. Nuestra búsqueda ha finalizado y se ha recuperado el elemento con solo tres accesos a nodos en lugar de ocho.


¿Qué es un PATRICIA trie?

Un PATRICIA trie es una variante de radix tries para el cual solo debería haber nnodos usados ​​para contener nelementos. En nuestro radix trie pseudocódigo crudamente demostrado anteriormente, hay cinco nodos en total: root(que es un nodo nullary; no contiene ningún valor real), root['e'], root['e']['d'], root['e']['s']y root['i']. En un ensayo PATRICIA solo debería haber cuatro. Echemos un vistazo a cómo estos prefijos pueden diferir mirándolos en binario, ya que PATRICIA es 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 ...

Consideremos que los nodos se agregan en el orden en que se presentan arriba. smile_itemes la raíz de este árbol. La diferencia, en negrita para que sea un poco más fácil de detectar, está en el último byte de "smile", en el bit 36. Hasta este punto, todos nuestros nodos tienen el mismo prefijo. smiled_nodepertenece a smile_node[0]. La diferencia entre "smiled"y "smiles"ocurre en el bit 43, donde "smiles"tiene un bit '1', también lo smiled_node[1]es smiles_node.

En lugar de usar NULLcomo ramas y / o información interna adicional para indicar cuándo termina una búsqueda, las ramas enlazan una copia de seguridad del árbol en algún lugar, por lo que una búsqueda termina cuando el desplazamiento para probar disminuye en lugar de aumentar. Aquí hay un diagrama simple de dicho árbol (aunque PATRICIA realmente es más un gráfico cíclico que un árbol, como verá), que se incluyó en el libro de Sedgewick mencionado a continuación:

Diagrama PATRICIA simple

Es posible un algoritmo PATRICIA más complejo que involucra claves de longitud variable, aunque algunas de las propiedades técnicas de PATRICIA se pierden en el proceso (es decir, que cualquier nodo contiene un prefijo común con el nodo anterior):

Diagrama PATRICIA complejo

Al ramificarse de esta manera, hay una serie de beneficios: cada nodo contiene un valor. Eso incluye la raíz. Como resultado, la longitud y complejidad del código se vuelve mucho más corta y probablemente un poco más rápida en realidad. Se sigue al menos una rama y la mayoría de las kramas (donde kestá el número de bits en la clave de búsqueda) para localizar un elemento. Los nodos son pequeños , porque almacenan solo dos ramas cada uno, lo que los hace bastante adecuados para la optimización de la ubicación del caché. Estas propiedades hacen de PATRICIA mi algoritmo favorito hasta ahora ...

Voy a acortar esta descripción aquí para reducir la gravedad de mi artritis inminente, pero si quieres saber más sobre PATRICIA puedes consultar libros como "El arte de la programación informática, volumen 3" de Donald Knuth. , o cualquiera de los "Algoritmos en {su-idioma-favorito}, partes 1-4" de Sedgewick.

autista
fuente
¿Podría ayudarme a comprender el significado del término "Radix"? Entiendo cómo, de forma natural, podemos intentar convertir un TRIE en un TRIE compacto permitiendo que varios símbolos / bordes se fusionen en un solo borde. Sin embargo, no puedo discernir por qué un TRIE no compactado (simplemente un TRIE) no puede denominarse Radix TRIE.
KGhatak
@ Seb - Realmente agradecería sus comentarios sobre la publicación stackoverflow.com/questions/40087385/… en Radix Tree. Gracias en adv.
KGhatak
@BuckCherry Me encantaría poder hacerlo, pero tenga en cuenta que, dado que me robaron la computadora, no podría poner el esfuerzo en una respuesta adecuada.
autista
18

TRIE:
Podemos tener un esquema de búsqueda en el que en lugar de comparar una clave de búsqueda completa con todas las claves existentes (como un esquema de hash), también podríamos comparar cada carácter de la clave de búsqueda. Siguiendo esta idea, podemos construir una estructura (como se muestra a continuación) que tiene tres llaves existentes: " papá ", " dab " y " cabina ".

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

(cab)   (dab)     (dad)

Este es esencialmente un árbol M-ario con nodo interno, representado como [*] y nodo hoja, representado como []. Esta estructura se llama trie . La decisión de ramificación en cada nodo se puede mantener igual al número de símbolos únicos del alfabeto, digamos R. Para alfabetos ingleses en minúsculas az, R = 26; para alfabetos ASCII extendidos, R = 256 y para dígitos binarios / cadenas R = 2.

TRIE compacto: por lo
general, un nodo en un trie usa una matriz con tamaño = R y, por lo tanto, provoca un desperdicio de memoria cuando cada nodo tiene menos bordes. Para eludir la preocupación por la memoria, se hicieron varias propuestas. Según esas variaciones, los trie también se denominan " trie compacto " y " trie comprimido ". Si bien una nomenclatura consistente es rara, una versión más común de un trie compacto se forma agrupando todos los bordes cuando los nodos tienen un solo borde. El uso de este concepto, el de arriba (fig-I) trie con las teclas “padre”, “DAB”, y “cabina” puede tomar siguiente formulario.

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

Tenga en cuenta que cada uno de 'c', 'a' y 'b' es el único borde de su correspondiente nodo principal y, por lo tanto, están conglomerados en un solo borde "cab". De manera similar, 'd' y a 'se combinan en un solo borde etiquetado como "da".

Radix Trie:
El término radix , en Matemáticas, significa la base de un sistema numérico, y esencialmente indica el número de símbolos únicos necesarios para representar cualquier número en ese sistema. Por ejemplo, el sistema decimal es la base diez y el sistema binario es la base dos. Usando un concepto similar, cuando estamos interesados ​​en caracterizar una estructura de datos o un algoritmo por el número de símbolos únicos del sistema de representación subyacente, etiquetamos el concepto con el término "base". Por ejemplo, "ordenación de base" para cierto algoritmo de ordenación. En la misma línea de lógica, todas las variantes de triecuyas características (como profundidad, necesidad de memoria, tiempo de ejecución de búsqueda fallida / acertada, etc.) dependen de la base de los alfabetos subyacentes, podemos llamarlas "trie" de base. Por ejemplo, un trie no compactado así como un trie compactado cuando usa alfabetos az, podemos llamarlo un trie de base 26 . Cualquier trie que use solo dos símbolos (tradicionalmente '0' y '1') se puede llamar un trie de base 2 . Sin embargo, de alguna manera muchas publicaciones restringieron el uso del término “Radix Trie” solo para el trie compactado .

Preludio de PATRICIA Tree / Trie:
Sería interesante notar que incluso las cadenas como claves se pueden representar usando alfabetos binarios. Si asumimos la codificación ASCII, entonces una clave "papá" se puede escribir en forma binaria escribiendo la representación binaria de cada carácter en secuencia, digamos " 01100100 01100001 01100100 " escribiendo formas binarias de 'd', 'a' y 'd' secuencialmente. Usando este concepto, se puede formar un trie (con Radix Two). A continuación, representamos este concepto utilizando una suposición simplificada de que las letras 'a', 'b', 'c' y'd 'son de un alfabeto más pequeño en lugar de ASCII.

Nota para la Fig-III: Como se mencionó, para facilitar la descripción, supongamos un alfabeto con solo 4 letras {a, b, c, d} y sus representaciones binarias correspondientes son "00", "01", "10" y "11" respectivamente. Con esto, nuestras teclas de cadena "papá", "dab" y "cab" se convierten en "110011", "110001" y "100001" respectivamente. El intento para esto será como se muestra a continuación en la Fig-III (los bits se leen de izquierda a derecha al igual que las cadenas se leen de izquierda a derecha).

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

PATRICIA Trie / Tree:
Si compactamos el trie binario anterior (Fig-III) usando compactación de un solo borde, tendría muchos menos nodos que los que se muestran arriba y, sin embargo, los nodos seguirían siendo más de 3, la cantidad de claves que contiene . Donald R. Morrison encontró (en 1968) una forma innovadora de usar trie binario para representar N claves usando solo N nodos y llamó a esta estructura de datos PATRICIA. Su estructura trie esencialmente eliminó los bordes simples (ramificación unidireccional); y al hacerlo, también se deshizo de la noción de dos tipos de nodos: nodos internos (que no representan ninguna clave) y nodos hoja (que representan claves). A diferencia de la lógica de compactación explicada anteriormente, este ensayo utiliza un concepto diferente en el que cada nodo incluye una indicación de cuántos bits de una clave se deben omitir para tomar una decisión de ramificación. Otra característica más de su prueba PATRICIA es que no almacena las claves, lo que significa que dicha estructura de datos no será adecuada para responder preguntas como, enumerar todas las claves que coinciden con un prefijo dado , pero es bueno para encontrar si existe una clave o no en el trie. No obstante, el término Patricia Tree o Patricia Trie, desde entonces, se ha utilizado en muchos sentidos diferentes pero similares, como para indicar un trie compacto [NIST], o para indicar un trie de radix con radix dos [como se indica en un sutil en WIKI] y así sucesivamente.

Trie que puede no ser un Radix Trie:
Ternary Search Trie (también conocido como Ternary Search Tree) a menudo abreviado como TST es una estructura de datos (propuesta por J. Bentley y R. Sedgewick ) que se parece mucho a un trie con ramificación de tres vías. Para dicho árbol, cada nodo tiene un alfabeto característico 'x', de modo que la decisión de ramificación depende de si un carácter de una clave es menor, igual o mayor que 'x'. Debido a esta función de ramificación fija de 3 vías, proporciona una alternativa de memoria eficiente para trie, especialmente cuando R (radix) es muy grande, como para los alfabetos Unicode. Curiosamente, el TST, a diferencia del trie (vía R) , no tiene sus características influenciadas por R. Por ejemplo, el error de búsqueda para TST es ln (N)a diferencia de log R (N) para R-way Trie. Los requisitos de memoria de TST, a diferencia de R-way trie, NO es una función de R también. Así que debemos tener cuidado de llamar a un TST un radix-trie. Personalmente, no creo que debamos llamarlo radix-trie ya que ninguna (hasta donde yo sé) de sus características está influenciada por la radix, R, de sus alfabetos subyacentes.

KGhatak
fuente
2
Como alguien que ha implementado PATRICIA de acuerdo con Morrison, Sedgewick y Knuth, puedo decirle que el algoritmo que ha descrito aquí (que también intenté describir en mi respuesta) sigue siendo muy adecuado para responder preguntas como enumerar todas las claves que coinciden con un determinado prefijo . PD: Es genial ver a alguien más en la pelota con respecto a la otra pregunta :) Me gusta esa explicación.
autista
Re "no será adecuado para responder preguntas como, enumerar todas las claves que coinciden con un prefijo determinado", ¿en serio?
Pacerier
@Pacerier ¡Seguro! PATRICIA clásica almacena un número entero, que puede usar como índice para una matriz. En la matriz pones la cadena. En el intento, pones el índice de matriz basado en 0 para la cadena. Haga que las funciones de búsqueda, comparación y extracción de bits operen sobre la cadena correspondiente al entero en lugar del entero, y si su función de inserción se basa en las demás (como debería ser, ya que hay mucha lógica repetida allí) y usted ' Estaré bien encaminado. También puede usarlo uintptr_tcomo su número entero , ya que ese tipo parece que normalmente se espera (aunque no es obligatorio) que exista.
autista
Afirma que "mucha literatura restringió el uso del término" Radix Trie "sólo para el trie compactado". En realidad, no puedo encontrar ninguna otra referencia que no sea wikipedia. ¿Encontraste otros?
wds
@ wds - Puede que tengas razón, ya que realmente no recuerdo cuáles son los recursos que mencioné cuando escribí esto. Una búsqueda rápida en Google me da enlaces como mathcs.emory.edu/~cheung/Courses/323/Syllabus/Text/trie02.html o tutorialsdiary.com/radix-trie-patricia-trie-or-compressed-trie que esencialmente apuntan a o (muy probablemente) derivado de / influenciado por wiki. Si encuentro algún otro recurso confiable / académico, lo publicaré aquí.
KGhatak