Estructuras de datos
Tablas de dispersión
Dra. Elisa Schaeffer [email protected]
PISIS / FIME / UANL
Tablas de dispersión dinámicas
Una
tabla de dispersi´on
(inglés: hash table) sonestructuras de datos que asocien
claves
convalores
. Por ejemplo, se podría implementar una guía telefónica por guardar los números (los valores) bajo los nombres (las claves).La idea es reservar primero espacio en la memoria de la computadora y después alocarlo a la información
insertada, de tal manera que siempre será rápido obtener la información que corresponde a una clave dada.
Función de dispersión
Una
funci´on de dispersi´on
o (funci´on hash
) es una función del conjunto de claves posibles a las direcciones de memoria.Su implementación típica es por arreglos
unidimensionales, aunque existen también variantes multidimensionales.
Tiempo de acceso
El tiempo de acceso promedio es
constante
por diseño. Hay que computar la función hash y buscar en la posición indicada por la función.En el caso que la posición indicada ya está ocupada, se puede por ejemplo asignar el elemento al espacio libre
siguiente, en cual caso el proceso de búsqueda cambia un poco: para ver si está o no una clave, hay que ir a la
posición dada por la función hash y avancar desde allá hasta la clave o en su ausencia hasta llegar a un espacio no usado.
Ajuste de tamaño
Cuando toda la memoria reservada ya está siendo
utilizada (o alternativamente cuando el porcentaje utilizado supera una cota pre-establecida), hay que reservar más. Típicamente se reserva una área
de tama ˜no doble
de lo anterior.Primero se copia todas las claves existentes con sus valores en la área nueva, después de que se puede empezar a añadir claves nuevas.
Resumen del análisis
“Casi todas” las operaciones de inserción son de tiempo constante.
El caso peor es Ω (n) para una inserción.
El caso peor para un total de n inserciones es Ω (n2).
Colas de prioridad
Una
cola de prioridad
es una estructura para guardar elementos con claves asociadas tal que el valor de la clave representa la “prioridad” del elemento.El menor valor corresponde a la prioridad más urgente.
Elemento mínimo
La operaciones de
colas de prioridad de adjunto
están enfocadas a lograr fácilmente averiguar el
elemento
m´ınimo
guardado (en el sentido de los valores de lasclaves), o sea, el elemento más importante.
=⇒ Es necesario los elementos tengan un orden.
Operaciones
1. insertar un elemento
2. consultar el elemento mínimo 3. retirar el elemento mínimo
4. reducir el valor de un elemento 5. juntar dos colas
Uso e implementación
También es posible utilizar el mismo dato como la clave en las aplicaciones donde solamente es de interés tener
acceso al elemento mínimo pero el concepto de prioridad no aplica.
Las colas de prioridad se puede implementar con montículos.
Estructuras unir-encontrar
Una estructura
unir-encontrar
(inglés: union-find) sirve para manejar un grupo C de conjuntos distintos deelementos.
Cada elemento debe tener un nombre único.
Operaciones básicas
form(i, S) que forma un conjunto S = {i} y lo añade
en C: C := C ∪ {S}; no está permitido que el elemento i pertenezca a ningún otro conjunto de la estructura,
find(i) que devuelva el conjunto S ∈ C de la
estructura donde i ∈ S y reporta un error si i no está incluido en ningún conjunto guardado,
union(S, T, U) que junta los dos conjuntos S ∈ C y
T ∈ C en un sólo conjunto U = S ∪ T y actualiza
C := (C \ {S, T}) ∪ {U}; recuerda que por definición S ∩ T = ∅.
Definición alternativa
No es realmente necesario asignar nombres a los
conjuntos, porque cada elemento pertenece a un sólo conjunto en C.
Entonces, se puede definir las operaciones también en la manera siguiente:
form(i): C := C ∪ {i},
find(i): devuelva la lista de elementos que estén en
el mismo conjunto con i,
union(i, j): une el conjunto en el cual pertenece i con
el conjunto en el cual pertenece j.
Conjuntos con árboles
Cada elemento está representada por un vértice hoja del árbol.
El vértice padre de unas hojas representa el conjunto de los elementos representadas por sus vértices hijos. Un subconjunto es un vértice intermedio, el padre de cual es su superconjunto.
El conjunto de todos los elementos es su propio padre.
Ejemplo
9 11 1 5 2 3 4 7 8 6 10 12Creación
La operación de crear un árbol nuevo es fácil: 1. Crear el primer vértice v.
2. Marcamos su clave (única) con c.
3. Hay que asignar que sea su propio padre: P(c) := c. Esto toma tiempo constante O (1).
Búsqueda
La operación de búsqueda del conjunto a cual pertenece una clave c, habrá que recorrir el árbol (o sea, el arreglo) desde le vértice con la clave deseada:
p := c;
mientras P(p) 6= p p := P(p);
devuelve p;
En el peor caso, el arrego se ha degenerado a una lista, por lo cual tenemos complejidad asintótica O (n).
Unión
Para juntar dos conjuntos, basta con hacer que la raíz de una (con clave c1) sea un hijo de la raíz de la otra (con clave c2): P(c1) := c2.
Esta operación necesita tiempo O (1).
Secuencias de operaciones
Podemos considerar la complejidad de una
secuencia de
operaciones
. Por ejemplo:1. creamos n conjuntos
2. ejecutamos por máximo n − 1 operaciones de unir dos conjuntos
3. ejecutamos no más de dos búsquedas de conjuntos por cada unión
Tiempo total: Θ(n + n − 1 + 2(n − 1)n) = Θ(n2).
Altura del árbol
Si aseguramos que el juntar dos árboles, el árbol de menor tamaño será hecho hijo del otro, se puede
demostrar que la altura del árbol que resulta es O (log n).
=⇒ Podemos asegurar que la operación de búsqueda del conjunto toma tiempo O (log n) y la secuencia analizada sería de complejidad asintótica O (n log n), que ya es
mejor.
Lo único que hay que hacer es guardar en un arreglo auxiliar el tamaño de cada conjunto y actualizarlo al unir conjuntos y al generar un nuevo conjunto.
Mejora
Aún mejor sería guardar información sobre la altura de cada árbol, la altura siendo un número menor o igual al tamaño.
La ventaja de la complejidad asintótica queda igual, pero solamente necesitamos O (log log n) bits para guardar la altura.
Caminos de búsqueda
Hay diferentes opciones para modificar los caminos cuando uno realiza una búsqueda de conjunto en la estructura.
Condensaci´on del camino:
vértices intermedios a la raízDivisi´on del camino:
vértices intermediados a otra parteCortar el camino a su mitad:
saltando a abuelosCondensación del camino
Traer los vértices intermedios a la raíz:
p := c; mientras P(p) 6= p p := P(p); q := i; mientras P(q) 6= q r := P(q); P(q) := p; q := r devuelve p;
División del camino
Mover vértices intermediados a otra parte:
p := c; mientras P(P(p)) 6= P(p) q := P(p); P(p) := P(P(p)); p := q devuelve y;