A. El valor es un int menor que el tamaño de la tabla hash. Por lo tanto, el valor es su propio hash, por lo que no hay una tabla hash. Pero si lo hubiera, sería O (1) y aún sería ineficiente.
Este es un caso en el que podría mapear trivialmente las claves a distintos depósitos, por lo que una matriz parece una mejor opción de estructura de datos que una tabla hash. Aún así, las ineficiencias no aumentan con el tamaño de la mesa.
(Es posible que aún use una tabla hash porque no confía en que los enteros permanezcan más pequeños que el tamaño de la tabla a medida que el programa evoluciona, desea hacer que el código sea potencialmente reutilizable cuando esa relación no se cumple, o simplemente no lo hace quieren que las personas que leen / mantienen el código tengan que desperdiciar su esfuerzo mental en comprender y mantener la relación).
B. Tienes que calcular un hash del valor. En esta situación, el orden es O (n) para el tamaño de los datos que se buscan. La búsqueda podría ser O (1) después de que hagas el trabajo de O (n), pero eso todavía me sale a O (n).
Necesitamos distinguir entre el tamaño de la clave (por ejemplo, en bytes) y el tamaño del número de claves que se almacenan en la tabla hash. Las afirmaciones de que las tablas hash proporcionan operaciones O (1) significan que las operaciones (insertar / borrar / buscar) no tienden a ralentizarse más a medida que la cantidad de claves aumenta de cientos a miles a millones a miles de millones (al menos no si todos los datos se accede / actualiza en un almacenamiento igualmente rápido, ya sea RAM o disco, los efectos de caché pueden entrar en juego, pero incluso el costo de una falla de caché en el peor de los casos tiende a ser un múltiplo constante del golpe en el mejor de los casos).
Considere una guía telefónica: puede tener nombres que sean bastante largos, pero ya sea que el libro tenga 100 nombres o 10 millones, la longitud promedio del nombre será bastante consistente y el peor de los casos en la historia ...
El récord mundial Guinness para el nombre más largo usado por alguien fue establecido por Adolph Blaine Charles David Earl Frederick Gerald Hubert Irvin John Kenneth Lloyd Martin Nero Oliver Paul Quincy Randolph Sherman Thomas Uncas Victor William Xerxes Yancy Wolfeschlegelsteinhausenbergerdorff, Senior
... wc
me dice que es 215 caracteres - Eso no es una fuerza superior, unidos a la longitud de la clave, pero no tiene que preocuparse acerca de que hay masivamente más.
Eso es válido para la mayoría de las tablas hash del mundo real: la longitud promedio de la clave no tiende a crecer con la cantidad de claves en uso. Hay excepciones, por ejemplo, una rutina de creación de claves puede devolver cadenas que incorporan números enteros en aumento, pero incluso entonces, cada vez que aumenta el número de claves en un orden de magnitud, solo aumenta la longitud de la clave en 1 carácter: no es significativo.
También es posible crear un hash a partir de una cantidad de datos clave de tamaño fijo. Por ejemplo, Visual C ++ de Microsoft se envía con una implementación de biblioteca estándar std::hash<std::string>
que crea un hash que incorpora solo diez bytes espaciados uniformemente a lo largo de la cadena, por lo que si las cadenas solo varían en otros índices, obtendrá colisiones (y, por lo tanto, en la práctica, comportamientos no O (1) en el lado de la búsqueda posterior a la colisión), pero el tiempo para crear el hash tiene un límite superior difícil.
Y a menos que tenga un hash perfecto o una tabla de hash grande, probablemente haya varios elementos por cubo. Por lo tanto, se convierte en una pequeña búsqueda lineal en algún momento de todos modos.
Generalmente es cierto, pero lo asombroso de las tablas hash es que la cantidad de claves visitadas durante esas "pequeñas búsquedas lineales" es, para el enfoque de encadenamiento separado para las colisiones, una función del factor de carga de la tabla hash (relación de claves a cubos).
Por ejemplo, con un factor de carga de 1.0, hay un promedio de ~ 1.58 para la duración de esas búsquedas lineales, independientemente del número de claves (vea mi respuesta aquí ). El hash cerrado es un poco más complicado, pero no mucho peor cuando el factor de carga no es demasiado alto.
Es técnicamente cierto porque no se requiere que la función hash utilice toda la información en la clave y, por lo tanto, podría ser un tiempo constante, y porque una tabla lo suficientemente grande puede reducir las colisiones a un tiempo casi constante.
Este tipo de pierde el punto. En última instancia, cualquier tipo de estructura de datos asociativa tiene que realizar operaciones en todas las partes de la clave a veces (la desigualdad a veces se puede determinar a partir de solo una parte de la clave, pero la igualdad generalmente requiere que se considere cada bit). Como mínimo, puede aplicar hash a la clave una vez y almacenar el valor hash, y si utiliza una función hash lo suficientemente fuerte, por ejemplo, MD5 de 64 bits, prácticamente podría ignorar incluso la posibilidad de que dos claves tengan el mismo valor (una empresa Trabajé para hacer exactamente eso para la base de datos distribuida: el tiempo de generación de hash aún era insignificante en comparación con las transmisiones de red en toda la WAN). Por lo tanto, no tiene mucho sentido obsesionarse con el costo de procesar la clave: eso es inherente al almacenamiento de claves independientemente de la estructura de datos y, como se dijo anteriormente, no lo hace.
En cuanto a tablas hash lo suficientemente grandes que reducen las colisiones, eso también está perdiendo el sentido. Para el encadenamiento por separado, todavía tiene una longitud de cadena de colisión promedio constante en cualquier factor de carga dado; es más alta cuando el factor de carga es más alto y esa relación no es lineal. El usuario de SO Hans comenta mi respuesta también enlazada arriba :
la longitud promedio del cucharón condicionada a cucharones no vacíos es una mejor medida de eficiencia. Es a / (1-e ^ {- a}) [donde a es el factor de carga, e es 2.71828 ...]
Por lo tanto, el factor de carga por sí solo determina la cantidad promedio de claves que colisionan en las que debe buscar durante las operaciones de inserción / borrado / búsqueda. Para el encadenamiento separado, no se trata solo de ser constante cuando el factor de carga es bajo, siempre es constante. Para el direccionamiento abierto, aunque su reclamo tiene cierta validez: algunos elementos en colisión se redirigen a depósitos alternativos y luego pueden interferir con las operaciones en otras claves, por lo que con factores de carga más altos (especialmente> .8 o .9), la longitud de la cadena de colisión empeora drásticamente.
Es cierto en la práctica porque con el tiempo funciona siempre que se elijan la función hash y el tamaño de la tabla para minimizar las colisiones, aunque eso a menudo significa no usar una función hash de tiempo constante.
Bueno, el tamaño de la tabla debería resultar en un factor de carga sensato dada la opción de hash cercano o encadenamiento separado, pero también si la función hash es un poco débil y las claves no son muy aleatorias, tener un número primo de cubos a menudo ayuda a reducir las colisiones también ( hash-value % table-size
luego se envuelve de tal manera que los cambios solo en un bit de orden superior o dos en el valor hash aún se resuelven en cubos distribuidos pseudoaleatoriamente en diferentes partes de la tabla hash).