You are on page 1of 22

UNIVERSIDAD NACIONAL AUTÓNOMA DE

MÉXICO

FACULTAD DE CONTADURÍA Y ADMINISTRACIÓN

Licenciatura En Informática

Estructura
s
de Datos
Autor: L.I. María de Lourdes Isabel
Ponce Vásquez
FEBRERO - JUNIO 2011

2
Contenido
UNIVERSIDAD NACIONAL AUTÓNOMA DE MÉXICO.............................................................1
FACULTAD DE CONTADURÍA Y ADMINISTRACIÓN..............................................................1
Licenciatura En Informática........................................................................................................1
Autor: L.I. María de Lourdes Isabel ............................................................................................1
UNIDAD 3. LISTAS.....................................................................................................................5
Introducción.............................................................................................................................5
Definición.................................................................................................................................5
Representación.......................................................................................................................5
Representación de Listas con Arreglos...............................................................................6
Representación de Listas Dinámicas..................................................................................6
Operaciones............................................................................................................................7
Lista Vacía...........................................................................................................................7
Lista Llena............................................................................................................................8
Agregar un elemento...........................................................................................................8
Eliminar un elemento...........................................................................................................8
Recorrer la lista....................................................................................................................9
Buscar un elemento.............................................................................................................9
Aplicaciones............................................................................................................................9
Representación de polinomios............................................................................................9
Solución de colisiones (hash)............................................................................................10
Lista Doblemente Enlazada..................................................................................................11
Definición...........................................................................................................................11
Representación..................................................................................................................11
Operaciones.......................................................................................................................12
Lista Circulares (Anillo)..........................................................................................................13
Definición...........................................................................................................................13
Representación..................................................................................................................13
Operaciones.......................................................................................................................14
Tablas de Dispersión.............................................................................................................16
Representación .................................................................................................................17
Elementos para una tabla hash.........................................................................................18
Operaciones.......................................................................................................................18
Inserción.............................................................................................................................18
Búsqueda...........................................................................................................................18
Borrar.................................................................................................................................18
Funciones Hash.................................................................................................................19
Hashing por residuo de la división.....................................................................................19
3.1.1.1. Hashing por doblamiento o pliegue......................................................................20
3.1.2. Tratamiento de colisiones........................................................................................21
Encadenamiento................................................................................................................21
Ventajas y desventajas......................................................................................................21
Aplicaciones.......................................................................................................................22

3
4
UNIDAD 3. LISTAS

Introducción
Anteriormente se mostraron los tipos de datos elementales los cuales tienen una
representación y manipulación estándar en la mayoría de los lenguajes de programación,
dentro de este grupo de datos se trabajó con arreglos, registros y apuntadores indicando
cómo funcionan, su representación y sus operaciones. Ahora toca el turno a los tipos de
datos compuestos que son aquellos que requieren la experiencia del programador para
indicar cómo funcionan, cómo se representan y las operaciones que se pueden realizar con
ellos, por supuesto es requisito tener bien claros los conceptos de los tipos de datos
elementales que servirán de base para la implementación de los tipos de datos compuestos.
En esta unidad se revisará el caso especial de las listas donde las operaciones de inserción y
eliminación se realizan sin restricciones, para después continuar con dos tipos particulares
de listas que son las filas y pilas donde estas operaciones tienen ciertas restricciones.

Definición
Una lista (list) es una secuencia de 0 o más elementos llamados generalmente nodos, de un
tipo dado almacenados en memoria. Son estructuras de datos lineales, donde cada elemento
de la lista, excepto el primero, tiene un único predecesor y cada elemento de la lista, excepto
el último, tiene un único sucesor.

El número de elementos de una lista se llama longitud. Si una lista tiene 0 elementos, se
denomina lista vacía.

En una lista se pueden agregar nuevos elementos o suprimir los elementos existentes en
cualquier posición a diferencia de las pilas y filas.

Aunque los datos en una lista pueden ser ingresados en cualquier orden, la facilidad de su
manejo permite mantener los datos ordenados en todo momento, lo cual le da mayor
potencial de uso.

En la vida diaria encontramos varios ejemplos de listas, una lista de pendientes, una lista de
alumnos, una lista de participantes en un evento, etc.

Representación
Las listas no son estructuras de datos básicas, por lo tanto requieren del ingenio del
programador para representarlas. Las listas pueden representarse mediante:

 Arreglos (Contiguas)
 Listas Ligadas (Enlazadas)

5
Representación de Listas con Arreglos
Para representar las listas con arreglos, se debe definir el tamaño máximo de la lista y una
variable auxiliar llamada LONGITUD, que será el apuntador al último elemento de la lista.

Lista
5 14 2
1 2 3 ... MÁX

LONGITUD
Lista Lista
1 5
2 14 3 2 LONGITUD

LONGITUD 3 2
2 14
1 5
Representación de Listas con Arreglos

Al emplear arreglos para implementar listas se tienen las mismas limitaciones de espacio de
memoria reservada propias de los arreglos; pudiendo provocar un error de desbordamiento
(overflow), así como el subdesbordamiento (underflow) si se intenta extraer un elemento de
una lista vacía. La inserción o eliminación de un elemento, excepto en el frente o al final de la
lista, necesitará recorrer los elementos de la lista una posición.

Las listas implementas con arreglos son llamadas Listas Contiguas.

Representación de Listas Dinámicas


La mejor forma de representar listas es precisamente mediante apuntadores, este tipo de
implementación proporciona mayor flexibilidad que las listas contiguas, ya que no es
necesario el desplazamiento de elementos cuando se realizan inserciones o eliminaciones.

Las listas implementadas de esta forma son llamadas Listas Enlazadas y pueden ser
elaboradas con un apuntador al primer elemento de la lista o con dos apuntadores, uno al
principio y otro al final de la lista.

PRIMERO

6
ÚLTIMO

Nulo
Representación de Listas Dinámicas
PRIMERO y ÚLTIMO indican la posición del primer y último elementos de la lista
respectivamente, los cuales pueden corresponder, o no, al primer y último elemento
agregado a la lista, ya que las inserciones se pueden realizar en cualquier lugar de ella.

Para agregar un nuevo elemento a una lista ordenada se requiere:

 Localizar la posición donde se desea agregar.


 Obtener la dirección del nuevo elemento (NUEVO) mediante una variable de
memoria dinámica.
 Hacer que el apuntador del nuevo nodo apunte al siguiente nodo de acuerdo a la
posición donde se va a agregar.
 Hacer que el nodo anterior apunte al nuevo nodo.
 Si el nodo agregado es el primero o el último se deberá mover el apuntador
correspondiente para indicar que ese es el primero o último de la lista.

En el caso de no poder obtener una localidad de memoria (NUEVO), se dice que la lista está
llena y se ha alcanzado la posición de overflow.

Para retirar un elemento de la lista se requiere:

 Localizar el elemento a retirar, si no se encuentra, se emitirá un mensaje que


indique que no se pudo localizar el dato.
 Guardar la posición de memoria a la que apunta el nodo a borrar.
 Hacer que la liga siguiente del nodo anterior al que se desea borrar, apunte al
siguiente nodo después del que se está borrando.
 Si el nodo borrado es el primero o último, se deberá mover el apuntador
correspondiente.
 Retornar la posición de memoria como disponible.
Operaciones
Algunas de las operaciones básicas que pueden realizarse sobre las listas son:

 Agregar un elemento (al principio, al final o en medio de la lista)


 Eliminar un elemento (al principio, al final o en medio de la lista)
 Recorrer la lista
 Buscar un elemento

Y las operaciones auxiliares:

 Verificar Lista Vacía


 Verificar Lista Llena
Lista Vacía

7
Esta operación auxiliar verifica que existan elementos en la lista, de ser así retorna un valor
FALSO y en caso contrario (si la lista no tiene elementos), retornará VERDADERO. Esta
operación es importante para evitar un error de underflow.

Lista Llena
Esta operación auxiliar verifica que exista espacio en la lista para poder agregar más
elementos, de ser así retorna un valor FALSO y en caso contrario (si la lista no tiene
espacio), retornará VERDADERO. Esta operación es importante para evitar un error de
overflow.

Agregar un elemento
Esta operación ingresa un elemento a la lista siempre y cuando la lista tenga todavía espacio
(ÚLTIMO < > MAX y PRIMERO < > 1 en el caso de arreglos, ó que la operación Reservar()
no retorne NULO para el caso de los apuntadores), después de agregar el elemento en la
lista, se puede dar el caso de tener que modificar el valor de PRIMERO o ÚLTIMO para
considerar el nuevo elemento. Al principio, la lista está vacía por lo que PRIMERO y ÚLTIMO
no apuntan a ningún elemento (PRIMERO = 0 para el caso de arreglos, PRIMERO = NULO
para el caso de los apuntadores).

La mayoría de las veces se necesita mantener ordenada la lista, por lo que muchas de las
inserciones se realizarán en medio de la lista, en el caso de los arreglos, esto exige el
movimiento de varios de sus elementos para conseguir un lugar donde se pueda ingresar el
nuevo valor.

Existen cuatro casos a considerar al momento de insertar en una lista:

 Insertar en una lista vacía


 Insertar al principio de la lista
 Insertar al final de la lista
 Insertar en medio (antes / después de un elemento)
Eliminar un elemento
Esta operación extrae un elemento de la lista siempre y cuando la lista no esté vacía
(PRIMERO < > 0 en el caso de arreglos, PRIMERO < > NULO en el caso de los
apuntadores), después de extraer el elemento de la lista, si éste ha sido extraído de la
primera o de la última posición de la lista, se deberá modificar el apuntador correspondiente
para apuntar al siguiente elemento contenido en la lista (el valor de PRIMERO se incrementa
ó el valor de ÚLTIMO se decrementa para el caso de arreglos, PRIMERO se coloca en la
posición apuntada por siguiente de PRIMERO o ÚLTIMO se coloca en el nodo que apunta a
ÚLTIMO, este proceso no es directo).

Existen cuatro casos a considerar al momento de eliminar en una lista:

 Eliminar en una lista que tiene un solo elemento


 Eliminar el primero de la lista
 Eliminar el último de la lista
8
 Eliminar en medio (antes / después de un elemento)
Recorrer la lista
La operación de recorrido consiste en visitar cada uno de los nodos que forman la lista. La
visita de un nodo puede definirse por medio de una operación muy simple (por ejemplo la
impresión de la información del mismo), o por medio de operaciones tan complejas como se
requiera.

Para recorrer todos los nodos de una lista se comienza con el PRIMERO. En el caso de los
arreglos, sólo es necesario incrementar el valor de un contador hasta llegar al ÚLTIMO
elemento de la lista. Con los apuntadores se toma el valor del campo liga del primer nodo, se
avanza al segundo; a su vez, el campo liga del segundo nos dará acceso al tercero, y así
sucesivamente. En general, la dirección de un nodo, excepto el PRIMERO, está dada por el
campo liga de su predecesor.

Debido a que las listas son estructuras de datos recursivas, pueden manejarse fácilmente
con procesos recursivos.

Buscar un elemento
La operación de búsqueda de un elemento en una lista organizada estáticamente se puede
realizar mediante una búsqueda binaria; si la lista está organizada dinámicamente, la
búsqueda debe ser en modo secuencial. Se deben recorrer los nodos, tomando el campo liga
como acceso al siguiente nodo a visitar. Este proceso se realiza implícitamente en las
operaciones definidas anteriormente de inserción y borrado, si se desea mantener la lista
ordenada.

Si la lista está ordenada, la búsqueda se realiza hasta encontrar el elemento o mientras el


elemento buscado sea menor al visitado, en las listas desordenadas se debe buscar
exhaustivamente hasta el último elemento de la lista para asegurarse si se encuentra o no en
ella.

Aplicaciones
Algunas de las aplicaciones computacionales de las listas más conocidas son:

 Representación de polinomios
 Resolución de colisiones (hash)

Representación de polinomios
En este caso, las listas se emplean para almacenar los coeficientes diferentes de cero del
polinomio junto al exponente. Por ejemplo:

P(x) = 3X4 + 0.5X3 + 6X - 4

9
PRIMERO ÚLTIMO

3 4 0.5 3 6 1 -4 0
Nulo
El nodo contiene dos campos de información, COCIENTE y EXPONENTE.

Solución de colisiones (hash)


Entre los métodos de búsqueda que existen, hay uno que se llama por transformación de
claves (hash), es un método en donde la información que se desea almacenar tiene una
clave, esta se convierte en una posición de memoria para poder tener acceso a la
información de manera directa, lo cual provoca que el método sea muy eficiente, sin
embargo, puede ocurrir que dos o más claves generen la misma dirección de memoria, a
esto se le llama colisión, en este caso, se puede generar una lista de todas las claves que
generan esa posición de memoria de manera que, cuando se busca en la dirección y si no se
encuentra la información buscada, se recorre la lista correspondiente para localizar el
elemento.

10
Lista Doblemente Enlazada
En las listas simples mostradas anteriormente se pudo observar que el proceso de
eliminación por ÚLTIMO es largo e ineficiente, ya que se deben recorrer todos los nodos
anteriores a él para llegar a su antecesor mediante apuntadores auxiliares y así poder
eliminarlo, para eliminar esta desventaja, y además tener acceso a los elementos en
cualquier orden, existen las listas doblemente enlazadas.

Definición
Una lista doblemente enlazada es una estructura de datos que tiene dos campos de tipo
apuntador, uno que señala al nodo sucesor (siguiente) y otro al antecesor (anterior). Su
recorrido puede realizarse tanto de PRIMERO a ÚLTIMO como de ÚLTIMO a PRIMERO.
Cada nodo en esta lista consta de mínimo un campo de información y otros dos de tipo
apuntador (anterior y siguiente), a su vez, es apuntado por dos nodos, el anterior y el
siguiente de él.

Al incluir los dos apuntadores, se logra un manejo más eficiente de las listas, ya que se
puede conocer desde cualquier nodo cuál es el nodo sucesor y antecesor, cosa que no se
puede hacer en las listas simples.

Para tener un acceso fácil a la información de la lista, se deben tener dos apuntadores, uno
al primer elemento y otro al último.

Representación
Aunque las listas doblemente enlazadas también se pueden representar mediante arreglos
de dos dimensiones, este tipo de representación resulta ser mucho más complejo que su
contraparte, la representación dinámica. Por esto, en este curso, sólo se tratará su
representación mediante apuntadores.
ÚLTIMO
PRIMERO

Nulo Nulo

Representación de Listas Doblemente Enlazadas

En este caso, también se requieren de preferencia dos apuntadores, uno al PRIMERO y otro
al ÚLTIMO elemento de la lista, y las inserciones se pueden realizar en cualquier lugar de
ella.

Para agregar un nuevo elemento a una lista doblemente enlazada de manera ordenada se
requiere:

 Localizar la posición donde se desea agregar.

11
 Obtener la dirección del nuevo elemento (NUEVO) mediante una variable de
memoria dinámica.
 Hacer que el apuntador siguiente del nuevo nodo apunte al siguiente nodo de
acuerdo a la posición donde se va a agregar.
 Hacer que el apuntador anterior del nodo siguiente apunte al nuevo nodo.
 Hacer que el apuntador siguiente del nodo anterior apunte al nuevo nodo.
 Hacer que el apuntador anterior del nuevo nodo apunte al nodo anterior.
 Si el nodo agregado es el PRIMERO o el ÚLTIMO se deberá mover el apuntador
correspondiente para indicar que ese es el primero o último de la lista.

En el caso de no poder obtener una localidad de memoria (NUEVO), se dice que la lista
doblemente enlazada está llena y se ha alcanzado la posición de overflow.

Para retirar un elemento de la lista doblemente enlazada se requiere:

 Localizar el elemento a retirar, si no se encuentra se emitirá un mensaje que


indique que no se pudo localizar el dato.
 Guardar la posición de memoria a la que apunta el nodo a borrar.
 Hacer que la liga siguiente del nodo anterior al que se desea borrar, apunte al
siguiente nodo después del que se está borrando.
 Hacer que la liga anterior del nodo siguiente al que se está borrando, apunte al
nodo anterior al que se está borrando
 Si el nodo borrado es el PRIMERO o ÚLTIMO, se deberá mover el apuntador
correspondiente.
 Retornar la posición de memoria como disponible.
Operaciones
Las operaciones que se llevan a cabo en esta estructura son esencialmente las mismas que
en una lista simple tomando en cuenta que ahora se debe enlazar con dos ligas cada
elemento de la lista, la excepción es la operación de recorrido, ya que esta se puede realizar
en ambos sentidos (hacia el frente y hacia atrás).

12
Lista Circulares (Anillo)
En las listas lineales enlazadas simples no se puede tener acceso mediante un elemento a
cualquier otro elemento antecesor de manera directa, siempre se debe colocar en el primer
elemento de la lista y partir de ahí para alcanzar cualquier otro elemento, en cambio, si en
vez de almacenar NULO en el apuntador del campo siguiente del último elemento se
almacena la posición del primer elemento de la lista, se podrá tener acceso a cualquier
elemento antecesor desde cualquier otro elemento.

Definición
Una lista circular es una estructura de datos en la cual, el elemento anterior al primero es el
último y el siguiente elemento del último es el primero.

Tanto las listas estáticas como las dinámicas y las simples como las dobles, pueden
implementarse mediante listas circulares.

Las listas circulares se logran haciendo que el apuntador siguiente del último elemento
apunte al primero, y en el caso de las listas doblemente enlazadas, el apuntador anterior del
primer elemento apunta al último elemento de la lista.

Al realizar el manejo de esta forma, sólo se requiere un apuntador al primer o de manera más
eficiente, al último elemento de la lista para realizar cualquier operación, ya que mediante él,
se pueden conocer el resto de los elementos.

Las listas circulares tienen las siguientes ventajas con relación a las listas enlazadas simples:

 Cada nodo de esta lista puede acceder a cualquier otro nodo. Esto permite recorrer
la lista completa a partir de cualquier nodo, en cambio la lista simple sólo se puede
recorrer totalmente si se encuentra en el primer nodo.
 Las operaciones de concatenar y dividir listas son más eficaces en estas listas que
en las enlazadas simples.

Desventaja:

 Pueden crearse ciclos infinitos al momento de recorrerla si no se tiene suficiente


cuidado para identificar el final de la lista.
Representación
Como se indicó anteriormente, las listas circulares también se pueden representar de manera
estática o dinámica.

En su representación estática, se permite, al momento de insertar si ya no hay espacio al


final del arreglo, pero hay espacio al principio del arreglo, mover el índice del último a la
primera posición del arreglo, de tal forma que puede haber situaciones donde el valor del
índice del último elemento de la lista sea menor que el del primero.

13
Lista
D E A B C

Último Primero

Representación Estática de Listas Circulares

La operación de agregar un elemento a la lista se realiza de manera similar a la lista simple,


excepto que:

 Para agregar un elemento antes de primero, éste se puede agregar aún cuando no
haya espacio a la izquierda de primero siempre y cuando se tenga espacio en la
última posición del arreglo.
 Para agregar un elemento después de último, si no hay espacio a la derecha de
último, se moverá último a la primera posición del arreglo.
 Por conveniencia, el primer elemento de la lista se coloca al centro del arreglo.

La representación de una lista circular simplemente enlazada requiere un solo apuntador al


primer o al último elemento de preferencia, tomando en cuenta que siempre el último
elemento de la lista apunte al primero.

Último
Representación Dinámica de Listas Circulares Simples

En este caso, como se puede notar, no existen apuntadores a nulo, ya que aún cuando sólo
exista un elemento, el último siempre apuntará al primero.

También las listas doblemente enlazadas pueden representarse de manera circular:

Último
Representación Dinámica de Listas Circulares Dobles

En este caso, el último elemento apunta al primero y el primero apunta al último, tampoco se
tienen nodos que apunten a nulo y cuando es el primer elemento, se apunta a sí mismo dos
veces.

Operaciones

14
Nuevamente, las operaciones a realizar sobre esta estructura son las mismas que en una
lista simple, tomando en cuenta que ahora se debe enlazar el último elemento al primero de
la lista (y el primero al último en el caso de las listas doblemente enlazadas) y no existirán
nodos apuntando a nulo.

15
Tablas de Dispersión
La búsqueda binaria proporciona un medio para reducir el tiempo requerido de búsqueda en
una lista. Este método, sin embargo, exige que los datos estén ordenados. Existe otro
método que puede aumentar la velocidad de búsqueda en el que los datos no necesitan
estar ordenados, este método se conoce como:

 Método de transformación de claves a dirección


 Técnicas de almacenamiento disperso ó dispersión
 Técnicas aleatorias
 Técnicas de direccionamiento directo
 Métodos de tabla de Hash
 Métodos de tablas de dispersión
 Métodos de mapas Hash
 Método Hashing

El término más usado es el de Hashing. Aunque existen amplios estudios sobre el tema, no
es la intención de esta unidad conocerlo a profundidad, por lo que sólo se tratarán los
conceptos importantes y relacionados con las listas.

El cálculo de dirección que es la aplicación de una función que traduce un conjunto


relativamente grande de posibles valores de clave, a un rango relativamente pequeño de
valores de direcciones relativas (como un diccionario), se le conoce como función hash.

El concepto de hashing fue usado inicialmente para manejar el acceso a las tablas de
símbolos en el núcleo, y después, para direccionar la ubicación de los registros en los
primeros dispositivos de almacenamiento de acceso directo. Desde entonces se han
realizado muchos trabajos que han contribuido al desarrollo de muchas técnicas para
instrumentar el concepto.

Estas técnicas son importantes no sólo en el direccionamiento de arreglos en memoria


principal, sino también para el direccionamiento de archivos relativos. Los cálculos se aplican
a las claves de los registros para calcular los subíndices de los registros respectivos en el
arreglo. Por lo que un registro puede encontrarse con una sola prueba (a menos que ocurran
colisiones); sin hacer búsqueda en el arreglo.

Un problema con este proceso, es que la función de transformación no puede ser de uno a
uno, las direcciones calculadas pueden no ser todas únicas.

Esta situación, donde:

R (K1) = R (K2) pero K1 <> K2

K1 y K2 son claves diferentes pero generan la misma dirección; a esto se le llama colisión.
Dos llaves no iguales (K1 y K2) que se transforman y obtienen la misma dirección relativa,
son llamadas sinónimos, y deben ser procesador para encontrar la dirección adecuada.

16
Mientras más uniforme sea la distribución de las claves, más uniformes serán las direcciones
calculadas. Al elegir una técnica de hashing deben considerarse los siguientes objetivos
principales:

 Cada una de las claves debe apuntar a una dirección en el espacio disponible de
almacenamiento.
 Las direcciones se deben distribuir de manera que se reduzca al mínimo el
número de sinónimos.
 El cálculo no debe ser demasiado complejo computacionalmente.

Cada uno de los elementos ha de tener una clave que identifica de manera única al
elemento. Por ejemplo, el campo número de cuenta de un conjunto de alumnos puede
considerarse un campo clave para organizar la información relativa al alumnado ya que el
número de cuenta es único. Hay una relación única (uno a uno) entre el campo y el registro
alumno. Podemos suponer que no existen, simultáneamente, dos registros con el mismo
número de cuenta.

Representación

Internamente, las tablas de dispersión son un arreglo. Cada una de las posiciones
del arreglo puede contener ninguna, una o varias entradas del diccionario.
Normalmente contendrá una como máximo, lo que permite un acceso rápido a los
elementos, evitando realizar una búsqueda en la mayoría de los casos. Para saber
en qué posición del arreglo se debe buscar o insertar una clave, se utiliza una
función de dispersión. Una función de dispersión relaciona a cada clave con un
valor entero. “Dos claves iguales deben tener el mismo valor de dispersión,
también llamado hash value, pero dos claves distintas pueden tener el mismo
valor de dispersión, lo cual provocaría una colisión”.

..

12

357

821

Las tablas hash se suelen implementar sobre arreglos de una dimensión, aunque se pueden
hacer implementaciones multi-dimensionales basadas en varias claves. Como en el caso de
los arreglos, las tablas hash proveen tiempo constante de búsqueda promedio, sin importar el
número de elementos en la tabla. Sin embargo, en casos particularmente malos el tiempo de
búsqueda puede llegar a ser muy alto, en función del número de elementos.

Comparada con otras estructuras de arreglos asociadas, las tablas hash son más útiles
cuando se almacenan grandes cantidades de información.

17
Con frecuencia, el tamaño del arreglo de las tablas hash es un número primo. Esto se hace
con el objeto de evitar la tendencia de tener divisores comunes con el tamaño de la tabla
hash, lo que provocaría muchas colisiones tras el cálculo. Sin embargo, el uso de una tabla
de tamaño primo no es un sustituto a una buena función hash.

Elementos para una tabla hash


Para implementar una tabla hash se requiere:

 Una estructura de acceso directo (arreglo o archivo)


 Una estructura de datos con una clave (registro)
 Una función hash cuyo dominio sea el espacio de claves
 Un mecanismo para solucionar las colisiones

Operaciones
Las operaciones básicas implementadas en las tablas hash son:

 Inserción (llave, valor)


 Búsqueda (llave) que devuelve valor
 Borrar (llave)

Las operaciones auxiliares que se pueden realizar sobre las tablas hash son:

 Recorrer (iteración)
 Vaciado

Inserción

 Para almacenar un elemento se convierte su clave a un número (aplicar función hash).


 El resultado de la función se mapea al espacio de direcciones del arreglo que se
emplea como soporte (se obtiene un índice). Y se verifica que no exista.
 El elemento se almacena en la posición de la tabla obtenida en el paso anterior.
o Si en la posición de la tabla ya había otro elemento, se ha producido una
colisión. Este problema se puede solucionar asociando una lista en cada
posición de la tabla.

Búsqueda

 Para recuperar los datos, es necesario únicamente conocer la clave del elemento, a la
cual se le aplica la función hash.
 El valor obtenido se mapea al espacio de direcciones de la tabla.
o Si el elemento existente en la posición indicada en el paso anterior tiene la
misma clave que la empleada en la búsqueda, entonces es el deseado. Si la
clave es distinta, se debe buscar en la lista de colisiones.

Borrar
18
 Para borrar un elemento, se convierte su clave a un número (aplicar función hash).
 El resultado de la función se mapea al espacio de direcciones del arreglo que se
emplea como soporte (se obtiene un índice). Y se verifica que exista.
 Se elimina de la lista.

Funciones Hash
La eficiencia de una función hash depende de:

 La distribución de los valores de las claves que realmente se usan.


 El número de valores de las claves que realmente están en uso con respecto al
tamaño de espacio de direcciones.
 El número de registros que pueden almacenarse en una dirección dada sin causar
colisiones (se supone 1).
 La técnica usada para resolver el problema de colisiones.

Existen muchas técnicas de cálculo de dirección; entre las más comunes se encuentran:

 Método de residuo de la división


 Método de cuadrado medio
 Método de doblamiento o pliegue

En general el proceso de hashing debe realizar lo siguiente:

 Representar la clave en forma numérica (tomando por ejemplo el código ASCII)


 Desglosar y sumar (tomar los números y sumarlos para obtener un solo número)
 Realizar el cálculo de la dirección
Hashing por residuo de la división
Algunas de las primeras investigaciones sobre hashing condujeron a una función hash
conocida como el método del residuo de la división, o simplemente de la división. Este
método parece ser el mejor en la mayoría de los casos. La idea de este método es la de
dividir el valor de la llave entre un número apropiado, y después utilizar el residuo de la
división como dirección relativa para el registro.

dirección relativa = clave módulo divisor + 1 ó


dirección relativa = clave módulo divisor

Mientras que el valor calculado real de una dirección relativa, dados tanto un valor de clave
como el divisor, es directo; la elección del divisor apropiado puede no ser tan simple. Existen
varios factores que deben considerarse para seleccionar el divisor:

1. El rango de valores que resultan de la operación "clave módulo divisor", va desde cero
hasta el divisor - 1. Luego, el divisor determina el tamaño del espacio de direcciones
relativas. Si se sabe que el arreglo va a contener por lo menos N registros, entonces
tendremos que hacer que divisor > n, suponiendo que solamente un registro puede
ser almacenado en una dirección relativa dada.

19
2. El divisor deberá seleccionarse de tal forma que la probabilidad de colisión sea
minimizada. Este es un objetivo difícil de alcanzar. Mediante investigaciones se ha
demostrado que los divisores que son números pares tienden a comportase
pobremente, especialmente con los conjuntos de valores de clave que son
predominantemente impares. Otras investigaciones sugieren que el divisor deberá ser
un número primo. Sin embargo, otras sugieren que los divisores no primos trabajan
tan bien como los divisores primos, siempre y cuando los divisores no primos no
contengan ningún factor primo menor de 20. En general, lo más común y adecuado es
elegir el número primo más próximo, pero no mayor que el espacio total de
direcciones.

Ejemplo: Si se considera un espacio máximo de direccionamiento de 5000 registros, se


puede determinar el número primo usado como divisor:

Max = 5000

Entonces, el divisor será 4999. Usando ese divisor se calculan las siguientes direcciones
correspondientes a los valores de las claves siguientes:

clave Dirección relativa


123456789 123456789 mod 4999 + 1 =
1486
987654321 1892
123456790 1487
555555555 1689
000000504 0505
100064183 4200
200120472 0505 **colisión**

3.1.1.1. Hashing por doblamiento o pliegue


En esta técnica el valor de la llave es separado en varias partes, cada una de las cuales
(excepto la última) tiene el mismo número de dígitos que tiene la dirección relativa objetivo.
Estas particiones son después plegadas una sobre otra y sumadas. El resultado, con el dígito
de mayor orden truncado, si es necesario, es la dirección relativa.

Por ejemplo, considerando el valor de clave: 123459876 para una dirección relativa de 4
dígitos.

a) Se separa la clave:

1 2345 9876

b) Se pliegan:

1
2345
9876

20
c) Se suman:

13221

d) Se trunca el dígito de mayor orden, por lo que la dirección relativa es:

3221

La siguiente tabla muestra varias direcciones calculadas por este método:

Clave Dirección relativa


123456789 3221
987654321 8999
555555555 6110
000000472 0472
117400000 2740
027000040 2740 **colisión**

3.1.2. Tratamiento de colisiones


Si dos llaves generan una dirección al mismo índice, los registros correspondientes no
pueden ser almacenados en la misma posición. En estos casos, cuando una casilla ya está
ocupada, debemos encontrar otra ubicación donde almacenar el nuevo registro, y hacerlo de
tal manera que podamos encontrarlo cuando se requiera.

Existen diversas técnicas de resolución de colisiones, entre las más populares son:

 El encadenamiento (listas ligadas)


 Direccionamiento abierto.
Encadenamiento
Con la técnica más simple de encadenamiento, cada posición del arreglo está unido a una
lista de los registros insertados que colisionan en la misma casilla. La inserción consiste en
encontrar la casilla correcta y agregar al final de la lista correspondiente. El borrado consiste
en buscar y quitar de la lista.

Ventajas y desventajas
 Ventajas. El potencial de las tablas hash o dispersas radica en la búsqueda de
elementos; conociendo el campo clave se puede obtener directamente la posición que
ocupa, y por consiguiente, la información asociada a dicha clave.
 Desventajas.
o No permiten algoritmos eficientes para acceder a todos los elementos de la
tabla, en su recorrido.
o Aumentar el tamaño del espacio de direccionamiento relativo creado al usar
una de estas funciones, implica cambiar la función hash, para que se refiera a
un espacio mayor y volver a cargar los datos.
21
o Una función hash mal diseñada generará muchas colisiones, lo que la hace
ineficiente.
o Si se reserva espacio para todos los elementos posibles, se consume más
memoria de la necesaria.

El estudio de las tablas hash acarrea el estudio de funciones hash o dispersión, que
mediante expresiones matemáticas permiten obtener direcciones según una clave que es el
argumento de la función.

Aplicaciones
Estas tablas son usadas en múltiples aplicaciones, como los arrays asociativos, criptografía,
procesamiento de datos y firmas digitales, entre otros.

Una buena función de hash es una que experimenta pocas colisiones en el conjunto
esperado de entrada; es decir que se podrán identificar unívocamente las entradas.

Muchos sistemas relacionados con la seguridad informática usan funciones o tablas de


dispersión o hash.

Tarea

Leer al menos dos fuentes adicionales sobre los temas vistos en esta unidad y hacer un resumen de esta
unidad (máximo 1 cuartilla de resumen, no olvidar bibliografía y conclusiones -en otra cuartilla).
Adicionalmente, explicar:
1. Qué son los tipos de datos compuestos y los compuestos lineales.
2. Investigar al menos dos métodos adicionales para calcular funciones hash.
3. Investigar al menos dos métodos adicionales de manejo de colisiones. (hacer un algoritmo)
4. Cuáles son las diferencias entre lista estática y dinámica
5. Cuáles son las diferencias entre lista lineal estática y lista circular estática
6. Cuáles son las diferencias entre lista lineal dinámica y lista circular dinámica
7. Cuáles son las diferencias entre lista lineal doblemente ligada y lista circular doblemente ligada

22

You might also like