¿Es posible acelerar una tabla hash utilizando árboles de búsqueda binarios para encadenar por separado?

11

Quiero implementar una tabla hash usando árboles de búsqueda binarios para reducir la complejidad de búsqueda en el proceso de encadenamiento separado de O (n) (usando la lista vinculada) a O (log n) (usando BST). ¿Se puede hacer esto y, en caso afirmativo, cómo? Sería más fácil de entender si la solución es paso a paso, la implementación de la lógica.

Quiero reducir el tiempo de búsqueda en la tabla hash (compilación usando un encadenamiento separado), pero al mismo tiempo no quiero que aumente el tiempo de inserción. Para mi proyecto, no puedo cambiar la función hash para reducir las colisiones. Pero debido a la escalabilidad, están ocurriendo colisiones. Estoy tratando de encontrar una solución, de modo que pueda trabajar de alguna manera con el mejor acceso e insertar el tiempo en caso de que ocurra una colisión ... es decir, administrar el estado actual de las cosas que reestructurar todo el algoritmo. Si no funciona, entonces tendrá que reestructurarse. Entonces, ¿alguna idea?

Aviral
fuente
44
Las tablas hash y los árboles de búsqueda binaria son contenedores diferentes . Por lo tanto, no puede hacer lo que sugiere (o está cometiendo un error terminológico).
Basile Starynkevitch
Supongo que podría poner un par hash / valor en cada nodo en un árbol ... pero eso sería una tabla hash mala o un árbol binario malo. Sin alguna aclaración sobre por qué quiere hacer esto y de qué quiere que sea capaz el resultado final, no estoy seguro de que esto sea realmente responsable.
Ixrec
1
@AK_: Sí, algo así, como dijiste. Quiero manejar las colisiones usando el árbol de búsqueda binario. He corregido un poco mi pregunta para aclararla.
Aviral
1
Tenga en cuenta que viene con la penalización de O (n log n) para cada inserción entonces. En general, cuando tiene una tabla hash que comienza a llenarse demasiado (y tiene cadenas más largas de lo que puede tolerar), reconstruye el hash. Si regularmente encuentra cadenas de más de 3 o 4, algo está mal.
3
Hay una gran variedad de variaciones en la tabla hash para la reducción de colisiones, direccionamiento abierto y cambio de tamaño dinámico de la tabla. El que se ajuste a sus requisitos es algo que deberá analizar. Su enfoque actual está cubierto por Encadenamiento separado con otras estructuras

Respuestas:

11

Lo que está pidiendo es posible dadas sus limitaciones.

Análisis

La fuerza de una tabla hash es su rápida búsqueda y velocidad de inserción. Para obtener esa velocidad, uno debe abandonar cualquier apariencia de orden en la tabla: es decir, todas las entradas están mezcladas. Una lista es aceptable para usar como entrada de la tabla porque, mientras que el recorrido es O (n), las listas tienden a ser cortas suponiendo que la tabla hash es lo suficientemente grande y los objetos almacenados en la tabla se combinan utilizando un algoritmo de hash de buena calidad.

Un árbol de búsqueda binario (BST) tiene una inserción y búsqueda rápidas en O (log 2 n). También impone una restricción a los elementos que almacena: debe haber alguna forma de ordenar los elementos. Dados dos elementos A y B almacenados en el árbol, debe ser posible determinar si A viene antes que B o si tienen un orden equivalente.

Una tabla hash no impone tal restricción: los elementos en una tabla hash deben tener dos propiedades. Primero, debe haber una manera de determinar si son equivalentes; segundo, debe haber una manera de calcular un código hash determinista. El orden no es un requisito.

Si los elementos de su tabla hash tienen un orden, puede usar un BST como entrada de la tabla hash para contener objetos con el mismo código hash (colisiones). Sin embargo, debido a que BST tiene búsqueda e inserción de O (log 2 n), eso significa que el peor de los casos para toda la estructura (tabla hash más BST) es técnicamente mejor que usar una lista como entrada de tabla. Dependiendo de la implementación de BST, requerirá más almacenamiento que una lista, pero probablemente no mucho más.

Tenga en cuenta que normalmente la sobrecarga y el comportamiento de un BST no aporta nada a la mesa en situaciones del mundo real como cubos de tabla hash, por lo que el pobre rendimiento teórico de una lista es aceptable. En otras palabras, la tabla hash compensa la debilidad de la lista al colocar menos elementos en cada lista (depósito). Sin embargo : el problema indicó específicamente que la tabla hash no puede aumentar de tamaño, y las colisiones son más frecuentes de lo que es típico en una tabla hash.

Implementación

No voy a poner código aquí porque, sinceramente, no es realmente necesario y de todos modos no dio un idioma.

Lo que haría es simplemente copiar cualquier tabla hash estándar que contenga la biblioteca estándar de su idioma en una nueva clase, luego cambiar el tipo de cubo de tabla de una lista a un árbol. Dependiendo del idioma y su biblioteca estándar, esto puede ser algo muy trivial.

Normalmente no recomendaría copiar y pegar codificaciones como esta. Sin embargo, es una manera fácil de obtener una estructura de datos probada en batalla muy rápidamente.


fuente
En términos asintóticos, el uso de un árbol binario para el manejo de colisiones no cambia el rendimiento esperado de una tabla hash, siempre que la tabla hash ya hiciera los trucos habituales para lograr el rendimiento de O (1) amortizado de todos modos. Cambiar el tamaño de la tabla hash para garantizar un buen rendimiento significa que también se espera que los artículos esperados por cubo (el tamaño de los árboles binarios) sean pequeños, por lo que terminará con el mismo O (1) amortizado esperado de cualquier manera. Incluso para el peor de los casos: sin que se especifique ninguna restricción de equilibrio, el peor de los casos para un árbol binario es que termina comportándose como una lista vinculada de todos modos.
Steve314
@ Steve314 Tenga en cuenta que el problema es que hay muchas colisiones, por lo que espera que un cubo contenga más elementos de los que normalmente tendría una tabla hash.
Buen punto, por ejemplo, para una tabla hash de tamaño constante con datos ilimitados, el rendimiento asintótico de la tabla hash es el mismo que el rendimiento asintótico del manejo de colisiones: la tabla hash solo cambia los factores constantes.
Steve314
@ Steve314 a la derecha, esencialmente si la tabla hash no puede limitar efectivamente el número de elementos en cada segmento, el rendimiento asintótico se degrada en cualquier estructura de subdatos que se use en cada segmento. Agregué un párrafo a mi respuesta para aclarar esto.
7

El uso de un árbol binario para el manejo de colisiones en una tabla hash no solo es posible, se ha hecho.

Walter Bright es mejor conocido como el inventor del lenguaje de programación D , pero también escribió una variante ECMAScript llamada DMDScript . En el pasado, un reclamo principal de DMDScript (o posiblemente un antepasado, creo recordar el nombre DScript) era que sus tablas hash tendían a superar a las de muchos idiomas similares. La razón: manejo de colisiones utilizando árboles binarios.

No recuerdo exactamente de dónde es esto, pero los árboles utilizados eran árboles binarios ingenuos, sin un esquema de equilibrio parcial (no AVL, rojo-negro o lo que sea) lo que tiene sentido ya que suponiendo que la propia tabla hash se redimensiona cuando se llena demasiado. no obtienes tasas absurdamente improbables de colisiones de hash, los árboles binarios siempre deben ser pequeños. Básicamente, el peor de los casos sigue siendo el mismo que usar una lista vinculada para el manejo de colisiones (excepto que paga el precio de dos punteros por nodo en lugar de uno), pero el caso promedio reduce la cantidad de búsqueda dentro de cada cubo de hash.

Steve314
fuente