Materia: Algoritmos y Programación II HASHING Emiliano Castagnari Andrés de Barbará Sebastián Santisi 82930 82497 82069 2.do cuatrimestre 2003 ÍNDICE Índice 1. Hashing 1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . 1.2. Resolviendo colisiones de hasheo por direccionamiento 1.3. Borrando elementos desde una tabla de hasheo . . . . 1.4. Tablas de hasheo encadenadas . . . . . . . . . . . . . 1.5. Eficiencia en los métodos de rehasheo . . . . . . . . . 1.6. Reordenamiento de la tabla de hasheo . . . . . . . . 1.7. Método de Brent . . . . . . . . . . . . . . . . . . . . 1.8. Árboles binarios de hasheo . . . . . . . . . . . . . . . 2. El Contenedor Asociativo de Hasheo de la Plantillas (STL) 2.1. Tipos nuevos del Contenedor . . . . . . . . 2.2. Notación . . . . . . . . . . . . . . . . . . . 2.3. Definiciones . . . . . . . . . . . . . . . . . 2.4. Funciones del Contenedor : . . . . . . . . . 2.5. Garantias de complejidad . . . . . . . . . . 2.6. Modelos en el contenedor : . . . . . . . . . 3. Bibliografı́a . . . . . abierto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 3 6 6 7 9 10 10 Bibloteca Estandar de . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 12 12 13 13 13 14 14 1 1 Hashing 1. Hashing Hash, x. There is no definition for this word - nobody knows what hash is” AMBROSE BIERCE (The Devil’s Dictionaty, 1906) 1.1. Introducción Supongamos que tenemos un registro que se encuentra guardado en una tabla con una correspondiente clave. Es necesario para operar sobre ese dato, tener que analizar cierto número de estos registros antes de obtener el que estamos buscando. Para mejorar nuestro rendimiento y llevarlo a un nivel óptimo nos convendrı́a analizar que distribución en la tabla y que método de búsqueda podrı́amos usar para no realizar comparaciones innecesarias. Si queremos que cada clave sea devuelta en un solo acceso, entonces el lugar de cada registro en la tabla solo puede depender de una sola clave y no depender de la localización de las otras claves como ocurre en un árbol. El método mas eficiente de organizar tal tabla, es un arreglo donde cada registro es almacenado a una distancia especifica desde la base de este. Por desgracia un sistema como el descrito anteriormente no es muy práctico. Por ejemplo, pensemos en una compañı́a que tiene que administrar un stock de 100 objetos diferentes, los cuales poseen un número de serie de siete(7) dı́gitos. Si usáramos indexación directa necesitarı́amos un arreglo de 10 millones de elementos y esto es claramente una pérdida inaceptable de espacio. Lo que necesitamos es un método que nos permita convertir una clave de un elemento en un entero que represente la distancia a la base del arreglo donde se encuentra el registro, y que, idealmente, no hallan dos claves que tengan el mismo registro final. Desafortunadamente no existe tal método, pero intentemos entonces encontrar uno que se le acerque lo suficiente al ideal y determinemos qué tipo de acción tomar cuando no se aproxima a este. A la función que transforma un clave en un ı́ndice de una tabla se le llama función de hasheo (hash function). Si h es una función de hasheo y clave es una clave entonces h(clave) es el hasheo de la clave. Si r es el registro cuya clave hashea en hr, entonces hr es la clave de hasheo de r. Supongamos ahora que una empresa que tiene 1000 partes diferentes en su stock y cada parte tiene un registro diferente. Entonces un arreglo indexado del 0 al 1000 es suficiente para poder guardar todo el archivo de stock. Los últimos tres 2 1.2 Resolviendo colisiones de hasheo por direccionamiento abierto números del número de registro de las partes nos va a servir como ı́ndice de los los registros en el arreglo. El método anterior tiene una falla. Supongamos que dos claves, c1 y c2, son aquellas tal que h(c1) y h(c2) son iguales. Claramente si c1 esta en la tabla, cuando se queramos incluir c2 se intentara acceder al el registro donde se encuentra c1. Dos registros no pueden ocupar la misma posición. Cuando esto ocurre se llama colisión de hasheo (hash collision) o hash clash. Hay dos formas básicas de resolver esto. La primera es llamada rehasheo (rehashing), que incluye usar una función de hasheo secundaria en la clave de hasheo del registro. La segunda, llamada encadenamiento (chaining), construye una lista enlazada de todos los elementos cuyas claves hashean en el mismo ı́ndice. Mas allá de esto, cabe decir que una buena función de hasheo es aquella que minimizan las colisiones y distribuye de forma uniforme los registros. Dejar espacios en blanco en un arreglo es ineficiente en termino de espacio, pero reduce sensiblemente la necesidad de resolver los hash clashes y, por lo tanto, gana en velocidad. Más adelante mostraremos las diferencias de rendimiento entre una tabla medianamente llena y una tabla llena; este es un tema no menor que requiere bastante análisis. Vale también destacar que el hecho de que hashing permita el acceso directo a los elementos de la tabla posee una falla muy seria. Los elementos de la tabla de hasheo no son guardados en forma secuencial por claves y no hay métodos prácticos para obtener las claves en alguna secuencia dada. 1.2. Resolviendo colisiones de hasheo por direccionamiento abierto Un método simple para resolver colisiones de hasheo es el de poner el registro en la siguiente posición disponible en el arreglo. Esta técnica es llamada comprobación lineal(lineal probing) y es un ejemplo de un método general para resolver colisiones de hasheo llamado rehasheo (rehashing) o direccionamiento abierto (open addresing). En general una función de rehasheo, rh, acepta un ı́ndice del arreglo para calcular otro. Si la posición h(clave) del arreglo ya se encuentra ocupada por un registro con una clave diferente, rh es aplicada al valor de h(clave)para encontrar otra posición donde el registro pueda ser guardado. Si esta última posición se encuentra también ocupada puede ser aplicada la función, nuevamente, para saber sı́ rh(rh(h(clave))) esta disponible. 3 1.2 Resolviendo colisiones de hasheo por direccionamiento abierto Notemos que puede ocurrir que en el rehasheo nunca se encuentre una posición disponible por lo que se seguirı́a intentando calcular sin ningún resultado infinitamente. Esto puede pasar por dos motivos. Primero que la tabla este completa, lo cual es fácilmente salvable contando las veces que se aplica la función y comparando contra el total de elementos de la tabla. Segundo existen posiciones libres en la tabla pero la función de rehasheo nunca las toca. Consideremos la situación donde las los impares están llenos, los pares vacı́os, y la función de rehasheo solo toca los impares. Una de las propiedades de la una buena función de rehasheo es aquella que para cualquier ı́ndice i, los sucesivos rehasheos rh(i),rh(rh(i)),etc, cubre enteros desde 0 hasta el tamaño de la tamtabla - 1. Existe otra forma de medir la eficiencia de la función de rehasheo. Consideremos el caso de un rehasheo lineal. Considerando que la función de hasheo produce ı́ndices de que son uniformemente distribuidos sobre intervalos de 0 y tamtabla 1. Cuando el arreglo está vacı́o veremos que cualquier registro será ingresado en la tabla. Luego, que se hayan realizado varias entradas y algunas colisiones hayan sido resueltas, lo anterior ya no será cierto. El efecto en el cual dos claves que hashean en diferentes valores compiten entre ellas en sucesivos rehasheos se llama agrupamiento primario (primary clustering). De hecho las funciones que dependen exclusivamente de los ı́ndices ha ser rehasheados causan agrupamiento primario. 4 1.2 Resolviendo colisiones de hasheo por direccionamiento abierto Una forma de resolver este problema es permitir que la función de rehasheo dependa del número de veces que la función fue aplicada a un valor particular de hasheo. De esta forma rh es una función de dos argumentos. rh(i,j) depende del entero de rehasheo i y de la clave que esta siendo rehasheada por j -esima ves. Otro método para solucionar este problema es usar permutación aleatoria de los números entre 1 y t ( donde t es igual a tamtabla -1 ), y dejar el j -esimo rehasheo de h(clave) ser (h(clave) + pj) % tamtabla. Un tercer método para eliminar el (primary clustering) (agrupamiento primario) es el de hacer que la función j -esima de rehasheo sea (h(clave) + 2 ) % tamtabla. Este método es llamado rehasheo cuadrático (quadratic rehash)... el cual cabe decir que si bien reduce bastante el riesgo de clustering tiene la desventaja de que en el mejor de los casos sólo cubre la mitad de las claves de la tabla y puede pasar de no poderse ingresar elementos nuevos cuando todavı́a queda espacio libre. Mientras que estos métodos eliminan agrupamiento primario no eliminan otro fenómeno llamado agrupamiento secundario (secundary clustering), en el cual diferentes claves que hashean al mismo valor sigue el mismo camino de rehasheo. Una forma de eliminar cualquier clase de agrupamiento es usar doble hasheo (double hashing), el cual involucra el uso de dos funciones de hasheo, h1(clave) y h2(clave). h1, la cual es conocida por función primaria de hasheo (primary hash function), es usada primero usada para determinar la posición en la cual el registro debe ser guardado. Si esa posición esta ocupada, la función de rehasheo rh(i,clave) = (i + h2(clave)) % tamtabla es usada sucesivamente hasta que se encuentre un elemento de la tabla vacı́o. Mientras h2(clave1) no iguale h2(clave2), registros con claves clave1 y clave2 no competirán por el mismo lugar. 5 1.3 Borrando elementos desde una tabla de hasheo 1.3. Borrando elementos desde una tabla de hasheo Es muy dificultoso borrar elementos de una tabla de hasheo que usa rehasheo para búsquedas e inserciones. Supongamos que el registro r1 está en la posición p. Para sumar un registro r2 cuya clave hashea en p, este debe ser insertado en la primera posición libre que se encuentra tras volver aplicar la función de hasheo en el ı́ndice obtenido, es decir, en la posición rh(p), rh(rh(p)), etc. Supongamos que r1 es borrado, entonces esa posición esta ahora vacı́a. Ahora si buscamos por r2, vamos a hacer rh(p), donde estaba r1, y la encuentra vacı́a por lo tanto piensa que r2 no esta en la tabla. Una solución posible a este problema es que marquemos a este registro como borrado y no como vacı́o. Entonces la búsqueda solo se detendrá si encuentra un registro vacı́o y no uno borrado. Pero esta solución solo será viable si nos encontramos con pocos registros borrados, ya que si este no fuera el caso, las búsquedas tocarı́an muchas posiciones marcadas como borradas antes de concluir. 1.4. Tablas de hasheo encadenadas Hasta el momento hemos planteado varias de las desventajas que conllevan las tablas de dirección abierta, muchas de estas desventajas no existen en las tablas de hasheo encadenadas. Como ya se dijo, el procedimiento consiste en hacer un ı́ndice general que se corresponda con los posibles valores de h(c1), este ı́ndice apunta a listas enlazadas. Luego, no existen colisiones dado que si dos claves hashean en el mismo ı́ndice, simplemente se añade una cadena (chain) a la lista. Tampoco existe el problema de la eliminación de claves, dado que solamente basta con eliminar el nodo correspondiente en la lista. La desventaja de este método con respecto al método de open adressing es que si demasiadas claves hashean en el mismo ı́ndice, las listas crecen considerablemente, convirtiendo el problema en una búsqueda lineal; y el otro inconveniente de este método, es que el acceso a un array es mucho más rápido que el acceso a una lista. 6 1.5 Eficiencia en los métodos de rehasheo 1.5. Eficiencia en los métodos de rehasheo Utilizando rehasheo, el promedio de comprobaciones depende en la función de hasheo y el método de rehasheo. Asumamos que todas las funciones de hasheo son uniformes. Si n es el número de elementos y tamtabla es el tamaño de la tabla. Si tamtabla es grande queda demostrado que en una obtención exitosa usando rehasheo lineal es aproximadamente: 2∗tamtabla 2∗tamtabla−n+2 Definiendo factor de carga, fc como n/tamtabla. Cuandopel factor de carga se aproxima a 1 esta formula es inútil y en cambio debemos usar 2 (pi ∗ tamtabla/8)+ 0, 33. Para una búsqueda no exitosa 0,5 / (1 - fc) 2 + 0,5 para tamaños de tablas grandes. Cuando la tabla esta llena el número máximo de comprobaciones a realizar es (tamtabla + 1) / 2. Para tablas pequeñas el número es razonable pero para tablas grandes este puede mejorarse eliminando el agrupamiento primario seteando rh(i,clave) a rh(i + hkey) % tamtabla como definimos antes o usando rehasheo cuadrático. Esto deja el número de comparaciones en aproximadamente 1 - log (1 - fc) - fc/2 para búsquedas no exitosas. Para tablas completas las búsquedas exitosas son en (tamtabla + 1) y las no exitosas se mantienen en orden (tamtabla + 1) / 2. El doble hasheo mejora la eficiencia eliminando el agrupamiento primario y el secundario. El hasheo uniforme es definido como cualquier esquema de hasheo en el cual cualquier elemento nuevo insertado tiene las mismas posibilidades de ser insertado en cualquiera de las posiciones libres de la tabla. Para este esquema teórico, puede ser probado que el tiempo de una búsqueda exitosa es aproximadamente log (1 - fc) / fc; y que una no exitosa requiere (tamtabla + 1) / (tamtabla + 1 - n) o aproximadamente 1 / (1 - fc) para tablas grandes. Para tablas completas el tiempo de búsqueda exitosas es de log * (tamtabla + 1) - 0,5 y para no exitosas (tamtabla + 1) / 2. 7 1.5 Eficiencia en los métodos de rehasheo Estos datos indican que el hasheo lineal debe ser evitado para tablas que están a más del 75 % de su capacidad, especialmente si las búsquedas son frecuentes, ya que el agrupamiento primario tiene un significativo impacto en los tiempos de búsquedas. En cambio el agrupamiento secundario sólo adiciona un 0,5 comprobaciones al número promedio requerido. Dado el hecho de que el doble hasheo requiere cálculos adicionales para determinar h2(clave), tal vez sea preferible aceptar la media comprobaciones extra y usar rh(i,clave) = (i + hclave) % tamtabla. Otra técnica que puede mejorar el método de rehasheo lineal es rehasheo lineal de secuencias divididas. Este método se basa en que cuando encontramos que h(clave) está ocupado, comparamos clave con la clave de kh encontrada en la posición h(clave). Si kh < h(clave), usamos la función de rehasheo i + c1 ; si kh > h(clave), usamos i + c2, como función de rehasheo. Esta técnica reduce el número de comprobaciones en búsquedas exitosas en más de un 50 % y en búsquedas no exitosas en mas de 80 %. De cualquier forma las formas de rehasheo no lineales son todavı́a mejores. Las tablas anteriores también demuestran que el gran gasto que conlleva tener tablas casi llenas para una búsqueda no exitosa. Las inserciones también requieren el mismo número de comparaciones que las búsquedas no exitosas. Cuando la tabla esta casi llena las inserciones se aproximan a una búsqueda secuencial y es peor que una la inserción en un árbol. 8 1.6 Reordenamiento de la tabla de hasheo Respecto de las tablas resueltas mediante chaining hay que decir que hay un detalle importante para comenzar; en las tablas de direccionamiento, fc es siempre menor a uno, dado que surge de la división del número de claves por el tamaño de la tabla. En el método de encadenamiento fc puede ser mayor que uno dado que no existen restricciones al número de claves a almacenar. Los tiempos promedios de búsqueda en tablas encadenadas son del orden de 1 + fc/2 para búsquedas exitosas y fc para búsquedas fallidas. Es evidente que las tablas encadenadas tienen buen rendimiento aún cuando el factor de carga es grande, cosa que no ocurre en las tablas de hashing de dirección abierta. Hay que decir algo muy importante acerca de la técnica de hashing, y esto es que en ningún momento el tiempo de búsqueda depende de la cantidad de claves sino que depende exclusivamente del fc; es decir, por más que en una tabla hayan millones de entradas, si el tamaño de la tabla es suficientemente grande y la función de hasheo es la adecuada, el tiempo de búsqueda estará siempre acotado y será el mismo que para una tabla con sólo 10 entradas y con un tamaño proporcional al anterior. Esto quiere decir que una tabla de hasheo permite realizar de búsquedas en un orden O(1) dado que no depende del tamaño de la entrada. 1.6. Reordenamiento de la tabla de hasheo Cuando la tabla esta casi completa, muchos de los elementos de la tabla no están en los lugares dados por sus claves de hasheo. Deben hacerse muchas comparaciones antes que de encontrar alguno de los elementos. Si el elemento no esta en la tabla, entonces la totalidad de las posiciones de rehasheo deben ser examinadas antes de que esta se determine. Existen varias técnicas por las cuales se puede remendar esta situación. El primer método descubierto por Amble y Knuth, dice que una serie de elementos que hashean en un mismo elemento se mantienen en orden descendiente de claves. Cuando buscamos por un elemento no es necesario rehashear repetidamente hasta obtener un elemento vacı́o sino que en cuanto obtenemos un elemento cuya clave es menor a la clave de búsqueda entonces sabemos que el elemento no se encuentra en la tabla de hasheo. Cuando insertamos un elemento en la tabla, si accedemos a una clave que es menor a nuestra clave, entonces remplazamos nuestra clave, la clave a insertar, por la menor, que se encuentra en la tabla, y continuamos el proceso de inserción con la clave menor. Una tabla ordenada de esta forma se 9 1.7 Método de Brent llama tabla ordenada de hasheo (ordered hash table). Usar una tabla ordenada de hasheo no cambia el número promedio de comparaciones necesarias para encontrar una clave que esta en una tabla, pero reduce significativamente el número de comparaciones para determinar que una clave no existe en una tabla. Puede demostrarse que el número total de comparaciones necesarias para una búsqueda exitosa y una no exitosa es la misma. Desgraciadamente el promedio de comparaciones requeridas para una inserción no se ve reducido en una tabla ordenada de hasheo e iguala el número requerido por una búsqueda no exitosa en una tabla no ordenada. Las inserciones en una tabla ordenada también requieren de una significante cantidad de modificaciones en la tabla de hasheo. 1.7. Método de Brent Richard P. Brent descubrió que el tiempo promedio de las búsquedas exitosas podı́a ser contenido a medida que una tabla se fuera llenando. La técnica de Brent esta basada en el echo de que una búsqueda exitosa es mucho más común que una inserción, por lo tanto, realizando un poco mas de trabajo en la inserción, ganamos tiempo en las búsquedas, dado una gran ventaja de rendimiento final. El método requiere rehasheo de los argumentos de búsqueda hasta que un espacio vacı́o es encontrado. Cada clave en el camino de rehasheo es a su ves rehasheada para determinar si ubicar alguna de ellas en un espacio vacı́o requerirá mas rehasheos. Si este es el caso, los argumentos de búsqueda remplazan la siguiente clave en la tabla y la clave existente es insertada en su espacio de rehasheo. 1.8. Árboles binarios de hasheo Otro método para mejorar el algoritmo de Brent es atribuido a Gonnet y a Munro y es llamado Árbol Binario de Hasheo. Cada nodo del árbol contiene un ı́ndice en la tabla de hasheo. Entonces el nodo raı́z del árbol será nodo(0),nodo(2 * i + 1) y nodo(2 * i + 2) serán su hijo derecho e izquierdo respectivamente. Los ı́ndices de la tabla de hasheo contenido en nodo(i) serán referenciados como ı́ndice(i), y su clave en esa posición como clave(i). Para explicar como se construye el árbol, primero definamos el ancestro derecho mas joven del nodo(i) como adj(i), que es el número de nodo del padre del mas joven de los ancestros del nodo(i) que es hijo derecho, (En la figura adj(11) es 0). Si un nodo no posee adj entonces su adj es -1 (menos uno). 10 1.8 Árboles binarios de hasheo El árbol binario es construido en orden numeral. ı́ndice(0) es seteado a h(clave). ı́ndice(i), para cada subsiguiente i, es seteado a rh(ı́ndice((i - 1)/2), clave(adj(i))). Este proceso continua hasta que clave(i) iguala CLAVENULA y una posición vacı́a es encontrada en la tabla. Una ves que el árbol a sido construido, las claves de los caminos desde la raı́z hasta los últimos nodos son reordenados en la tabla de hasheo. emphi es inicializado en la posición del ultimo nodo del árbol. Luego si adj(i) no es cero, clave(adj(i)) y su registro asociado es movido desde la tabla[(ı́ndice(adj(i)))] a tabla[ı́ndice(i)] y i es reseteado a adj(i). Este proceso es repetido hasta que adj(i) es -1(menos uno), en cuyo caso clave y registro son insertado en tabla[ı́ndice(i)] y la inserción esta completa. Cuando buscamos subsecuentemente por claves, dos posiciones de tablas son probadas: a y b. Cuando buscamos por la tabla[b].clave, dos comprobaciones más son requeridas. Cuando buscamos por tabla[k].clave una comprobación es requerida. Un total de 5(cinco) posiciones son comprobadas, en toda la tabla de hasheo, cuando insertamos una clave; mientras que 6(seis) son requeridas si hubiera sido insertada en su camino de rehasheo. Vemos que todo el algoritmo depende de la función que encuentra el adj(i). Esta puede derivar del siguiente método para que se realice rápidamente: Encontrar la representación binaria de i + 1. Borrar cualquier TRAILING de 0(cero) bits y 1 bits precediéndolos. Restar 1(uno) del resultado del número binario a obtener su adj(i). Por ejemplo : la representación de 11(once) + 1(uno) es 1100. removiendo el TRAILING de 100 se llega a 1; entonces adj(11) = 0, adj(17) = 3, adj(14) = 6, etc. Gonnet y Munro obtienen resultados que se son más cercanos al óptimo que el algoritmo de Brent. De cualquier forma no son óptimos, ya que los elementos de 11 2 El Contenedor Asociativo de Hasheo de la Biblioteca Estándar de Plantillas (STL) tabla de hasheo solo pueden ser reordenados moviéndolos a posiciones mas altas de la tabla y nunca a las posiciones más bajas. Cuando la tabla esta cargada en factor 0,9, el árbol binario requiere 1,75 comprobaciones por obtención (en Brent 1.80), con un factor de 0,95 requiere 1.88 (en Brent 1,97). Para una √ tabla llena requiere un promedio de 2,13 y Brent 2,5. Mientras que Brent es O( 2 n) el Árbol Binario de Hasheo es de O(log n). 2. El Contenedor Asociativo de Hasheo de la Biblioteca Estándar de Plantillas (STL) 2.1. Tipos nuevos del Contenedor El contenedor asociativo de hasheo es lo que nos brinda la STL para manejar hashing. Junto con este contenedor aparecen dos tipos de datos mas: X::hasher() que nos da un modelo de función de hasheo cuyo argumento es del tipo key type, y X::value type la cual nos da la función de comparación o comprobación de claves, que tiene un predicado binario cuyo argumento debe ser del tipo X::key type. Con esta última un objeto del tipo key equal es devuelto si los argumentos de la función son la misma clave, y falso de otra forma. X::key equal debe ser una relación de equivalencia. 2.2. Notación X es un Tipo que es un modelo de Contenedor Asociativo de Hasheo. a es un Objeto del tipo X. t es un Objeto del tipo X::value type. k es un Objeto del tipo X::key type. p,q son Objetos del tipo X::iterator. n es un Objeto del tipo X::size type. h es un Objeto del tipo X::hasher. c es un Objeto del tipo X::key equal 12 2.3 Definiciones 2.3. Definiciones Los elementos del Contenedor Asociativo de Hasheo son organizados en bucket (buckets). El contenedor utiliza el valor de la función de Hasheo para determinar a cual bucket se le asignará. 2.4. Funciones del Contenedor : 2.5. Garantı́as de complejidad El contenedor nos brinda ciertas garantı́as de complejidad para las operaciones de sus funciones, las cuales son : Todos los constructores son amortizados en un tiempo constante. Las funciones de comprobación y de hasheo están amortizadas en tiempo constante. 13 2.6 Modelos en el contenedor : La complejidad promedio para eliminar una clave es de O(count(clave)). En el peor caso es lineal con el tamaño del contenedor. Borrar elementos esta amortizado en tiempo constante. El promedio de complejidad para borrar un rango es de O(n), donde n es la longitud del rango. El promedio de complejidad para la búsqueda es de tiempo constante. En el peor por caso es lineal con el tamaño del contenedor. El promedio de complejidad para la comparación de rangos es de O(count(clave)). El peor caso es lineal con el tamaño del Contenedor. El conteo de bucket esta amortizado en tiempo constante. Cambiar el tamaño del Contenedor es lineal con el tamaño del contenedor. 2.6. Modelos en el contenedor : hash set: Guarda claves del tipo Key. Pertenece también al Contenedor Asociativo Único; no permite que dos claves comparen igual. hash map: Asocia objetos del tipo Key con objetos del tipo Data. Pertenece también al Contenedor de Pares Asociados lo que significa que su tipo de valor es pair¡const Key, Data¿.También es un Contenedor Asociativo Único. hash multiset: Es un Contenedor Asociativo Múltiple que permite dos o más claves comparen de la misma forma. hash multimap: Posee las mismas funciones que el hash map pero es un Contenedor Asociativo Múltiple. 3. Bibliografı́a Andrew S. Tanenbaum - ”Structured Computer Organization, 4th ed.” http://www.cs.vu.nl/ ast/ 14 3 Bibliografı́a Donal Ervin Knuth - ”The Art Of Computer Programming” - Volume 3 - Sorting and Searching - Second Edition - 1998. Silicon Graphics Computer Systems, Inc. - Standard Template Library Programmer’s Guide http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi/srch24@ standar %20template %20library/0650/bks/SGI Developer/books/ STL PG/sgi html/index.html Kruse - ”Data Structures And Program Design”- 1st Edition - Prentice-Hall, Inc. - 1984. Kruse - ”Data Structures And Program Design In C++” - 1st Edition - PrenticeHall, Inc. - 2000 Menezes, van Oorschot, Vanstone - ”Handbook Of Applied Cryptography” CRC Press - 1997. http://www.cacr.math.uwaterloo.ca/hac Sedgewick; ”Algorithms” - Addison-Wesley - 1983. 15
Puede agregar este documento a su colección de estudio (s)
Iniciar sesión Disponible sólo para usuarios autorizadosPuede agregar este documento a su lista guardada
Iniciar sesión Disponible sólo para usuarios autorizados(Para quejas, use otra forma )