¿Cómo elijo entre una tabla hash y un Trie (árbol de prefijos)?


134

Entonces, si tengo que elegir entre una tabla hash o un árbol de prefijos, ¿cuáles son los factores discriminantes que me llevarían a elegir uno sobre el otro? Desde mi punto de vista ingenuo, parece que usar un trie tiene algo de sobrecarga adicional ya que no está almacenado como una matriz, pero que en términos de tiempo de ejecución (suponiendo que la clave más larga es la palabra inglesa más larga) puede ser esencialmente O (1) (en relación con el límite superior). ¿Quizás la palabra inglesa más larga tiene 50 caracteres?

Las tablas hash son de búsqueda instantánea una vez que obtiene el índice . Sin embargo, tener la clave para obtener el índice parece que podría tomar fácilmente cerca de 50 pasos.

¿Alguien puede proporcionarme una perspectiva más experimentada sobre esto? ¡Gracias!


1
Vale la pena señalar que un árbol redix es más eficiente que un árbol simple porque no necesita una nueva rama para cada byte de cadena. Además, los árboles redix brindan soporte para búsquedas "difusas" mejor que las tablas hash porque estás mirando bits individuales cuando trabajas en el camino. Por ejemplo, 00110010podría ser el byte de entrada, pero desea incluir la coincidencia 00111010que solo se elimina un bit.
Xeoncross

Respuestas:


116

Ventajas de los intentos:

Los basicos:

  • Tiempo de búsqueda predecible de O (k) donde k es el tamaño de la clave
  • La búsqueda puede tomar menos de k tiempo si no está allí
  • Apoya el recorrido ordenado
  • No hay necesidad de una función hash
  • La eliminación es sencilla

Nuevas operaciones:

  • Puede buscar rápidamente prefijos de claves, enumerar todas las entradas con un prefijo dado, etc.

Ventajas de la estructura vinculada:

  • Si hay muchos prefijos comunes, se comparte el espacio que requieren.
  • Los intentos inmutables pueden compartir estructura. En lugar de actualizar un trie en su lugar, puede construir uno nuevo que sea diferente solo a lo largo de una rama, en otro lugar, apuntando al antiguo trie. Esto puede ser útil para la concurrencia, múltiples versiones simultáneas de una tabla, etc.
  • Un trie inmutable es compresible. Es decir, también puede compartir estructura en los sufijos , mediante hash-consing.

Ventajas de las tablas hash:

  • Todos conocen las tablas hash, ¿verdad? Su sistema ya tendrá una implementación agradable y bien optimizada, más rápida que los intentos para la mayoría de los propósitos.
  • Sus llaves no necesitan tener ninguna estructura especial.
  • Más espacio eficiente que la estructura de trie vinculada obvia ( ver comentarios a continuación )

27
no puede estar del todo de acuerdo con "Más eficiente en espacio que la estructura de trie vinculada obvia": en una implementación de tabla hash general, ocupa un espacio mucho más grande para contener claves, mientras que en los intentos, cada nodo representa una palabra. En este sentido, los intentos son más eficientes en cuanto al espacio.
galactica

1
¿Qué hay de acceder a los datos de una estructura frente a la otra? Estoy pensando en caché y ubicación
Horia Toma

8
@galactica, eso está en conflicto con mi experiencia: por ejemplo, en esta respuesta de todas las estructuras que medí para el espacio, a un trie le fue peor. Esto tiene sentido ya que un puntero es mucho más grande que un byte. Sí, el intercambio de prefijos ayuda, pero debe superar muchos gastos generales para alcanzar la paridad. Una representación más eficiente en el espacio puede ayudar mucho, pero ya no estamos hablando de la estructura vinculada obvia.
Darius Bacon

1
@DariusBacon manejando planes de numeración telefónica parece un escenario razonable para los intentos. Escenario de muestra: coincidencia de número de teléfono con operador incluido. números portados de un transportista a otro. Para los diccionarios habituales puede depender del idioma (mandarín vs inglés), necesitaría n-gramos y / u otros datos estadísticos. Para un libro de rimas, un árbol de sufijos también parece una buena opción.
mbx

La diversidad de los datos a buscar es muy importante. Si un gran porcentaje de sus valores de datos son únicos, la complejidad de su espacio aumentará sobre el hash debido al uso de punteros nulos adicionales.
Estadísticas de aprendizaje por ejemplo

45

Todo depende de qué problema estés tratando de resolver. Si todo lo que necesita hacer es inserciones y búsquedas, vaya con una tabla hash. Si necesita resolver problemas más complejos, como consultas relacionadas con prefijos, entonces un trie podría ser la mejor solución.


8
si la tabla hash y el trie tienen la misma complejidad en la consulta, O (k) para la cadena de longitud k ¿por qué deberíamos elegir hash? ¿podría explicar por favor?
Sazzad Hissain Khan

29

Todo el mundo conoce la tabla hash y sus usos, pero no es exactamente un tiempo de búsqueda constante, depende de qué tan grande sea la tabla hash, la complejidad computacional de la función hash.

Crear enormes tablas hash para una búsqueda eficiente no es una solución elegante en la mayoría de los escenarios industriales en los que incluso la latencia / escalabilidad pequeñas son importantes (p. Ej., Comercio de alta frecuencia). Debe preocuparse por las estructuras de datos que se optimizarán para el espacio que ocupa en la memoria también para reducir la pérdida de caché.

Un muy buen ejemplo donde trie se adapta mejor a los requisitos es el middleware de mensajería. Tiene un millón de suscriptores y editores de mensajes en varias categorías (en términos de JMS: temas o intercambios); en tales casos, si desea filtrar mensajes basados ​​en temas (que en realidad son cadenas), definitivamente no desea crear una tabla hash para el millón de suscripciones con millones de temas. Un mejor enfoque es almacenar los temas en trie, por lo que cuando el filtrado se realiza en función de la coincidencia de temas, su complejidad es independiente del número de temas / suscripciones / editores (solo depende de la longitud de la cadena). Me gusta porque puedes ser creativo con esta estructura de datos para optimizar los requisitos de espacio y, por lo tanto, tener una menor pérdida de caché.


11

Usa un árbol:

  1. Si necesita la función de autocompletar
  2. Encuentre todas las palabras que comienzan con 'a' o 'ax', etc.
  3. Un árbol de sufijos es una forma especial de un árbol. Los árboles de sufijos tienen una lista completa de ventajas que el hash no puede cubrir.

4

Hay algo que no he visto a nadie mencionar explícitamente que creo que es importante tener en cuenta. Tanto las tablas hash como los intentos de varios tipos suelen tener O(k)operaciones, donde kes la longitud de la cadena en bits (o equivalente en caracteres).

Esto supone que tiene una buena función hash. Si no desea que "granja" y "animales de granja" hagan hash con el mismo valor, entonces la función hash tendrá que usar todos los bits de la clave, por lo que el hash "animales de granja" debería tomar aproximadamente el doble de tiempo "farm" (a menos que esté en algún tipo de escenario de hash rodante, pero también hay escenarios de ahorro de operación algo similares con intentos). Y con un trie de vainilla, está claro por qué insertar "animales de granja" tomará aproximadamente el doble de tiempo que solo "granja". A la larga, también es cierto con los intentos comprimidos.


3

La inserción y búsqueda en un trie es lineal con la longitud de la cadena de entrada O (s).

Un hash le dará un O (1) para búsqueda e inserción, pero primero debe calcular el hash en función de la cadena de entrada que nuevamente es O (s).

Conclusión, la complejidad del tiempo asintótico es lineal en ambos casos.

El trie tiene algo más de gastos generales desde la perspectiva de los datos, pero puede elegir un trie comprimido que lo pondrá de nuevo, más o menos en un empate con la tabla hash.

Para romper el empate, hágase esta pregunta: ¿Necesito buscar solo palabras completas? ¿O debo devolver todas las palabras que coinciden con un prefijo? (Como en un sistema de ingreso de texto predictivo). Para el primer caso, ve por un hash. Es un código más simple y limpio. Más fácil de probar y mantener. Para un caso de uso más elaborado en el que los prefijos o sufijos importan, elija un trie.

Y si lo haces solo por diversión, la implementación de un trie daría un buen uso a un domingo por la tarde.


"Un hash le dará un O (1) para la búsqueda y la inserción, pero primero debe calcular el hash en función de la cadena de entrada que nuevamente es O (s)". Gracias por explicar esto!
abadawi

Calcular la función hash no es O (s). En realidad es O (1). No necesita todos los bits de la cadena para calcularlo, algunos de ellos (un número constante de ellos) es suficiente.
Nicola Amadio

2

La implementación de HashTable ahorra espacio en comparación con la implementación básica de Trie . Pero con las cadenas, el orden es necesario en la mayoría de las aplicaciones prácticas. Pero HashTable perturba totalmente el orden lexográfico. Ahora, si su aplicación está realizando operaciones basadas en el orden lexográfico (como búsqueda parcial, todas las cadenas con el prefijo dado, todas las palabras en orden ordenado), debe usar Intentos. Para solo buscar, se debe usar HashTable (ya que podría decirse que proporciona un tiempo de búsqueda mínimo).

PD: Aparte de estos, los árboles de búsqueda ternarios (TST) serían una excelente opción. Su tiempo de búsqueda es más que HashTable, pero es eficiente en todas las demás operaciones. Además, es más eficiente en espacio que los intentos.


-2

Algunas aplicaciones (generalmente integradas, en tiempo real) requieren que el tiempo de procesamiento sea independiente de los datos. En ese caso, una tabla hash puede garantizar un tiempo de ejecución conocido, mientras que un trie varía según los datos.


66
La mayoría de las tablas hash no garantizan un tiempo de ejecución conocido: el peor de los casos es O (n), si cada elemento choca y se encadena
Adam Rosenfield

2
Para cualquier conjunto de datos, puede calcular una función hash perfecta que garantice las búsquedas de O (1) para esos datos. Por supuesto, calcular el hash perfecto no es gratis.
George V. Reilly

55
Además, el encadenamiento no es la única forma de manejar las colisiones; hay todo tipo de formas interesantes e ingeniosas para manejar esto (hash de cuco ( en.wikipedia.org/wiki/Cuckoo_hashing ) para uno) y la mejor opción depende de las necesidades del código del cliente.
Hank Gay

no sabía sobre el hash de cuco y su relación con el filtro de floración, será una lectura interesante, ¡gracias!
Horia Toma

No te olvides de Robin-hood Hashing, que es superior para la memoria caché y la variación. sebastiansylvan.com/2013/05/08/… codecapsule.com/2013/11/11/robin-hood-hashing
Jarred Nicholls
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.