Está en la página 1de 19

Estructura de Datos

El árbol binario de búsqueda facilita la eliminación de duplicados.

Conforme se crea el árbol, cualquier intento para insertar un valor duplicado será detectado
porque en cada una de las comparaciones un duplicado seguirá las mismas decisiones “ir a la izquierda”
o “ir a la derecha” que utilizó el valor original. Entonces, el duplicado eventualmente será comparado
con un nodo que contenga el mismo valor. Llegado a este punto el valor duplicado pudiera
simplemente ser descartado.

También es rápido buscar en un árbol binario un valor que coincida con un valor clave.

Si el árbol es denso, entonces cada nivel contendrá aproximadamente dos veces tantos
elementos como el nivel anterior. Por lo tanto un árbol binario de búsqueda con n elementos tendría un
máximo de log2n (logaritmo de base 2 de n niveles) y, por lo tanto, tendrían que efectuarse un máximo
log2n de comparaciones, ya sea para encontrar una coincidencia, o para determinar que no existe
ninguna.

El árbol binario de búsqueda es una estructura sobre la cual se pueden realizar eficientemente
las operaciones de búsqueda, inserción y eliminación.

Comparando esta estructura con otras, pueden observarse ciertas ventajas:

En un arreglo es posible localizar datos eficientemente si los mismos se encuentran


ordenados, pero las operaciones de inserción y eliminación resultan costosas.

En las listas, las operaciones de inserción y eliminación se pueden llevar a cabo con
facilidad, sin embargo la búsqueda es una operación bastante costosa que incluso nos puede
llevar a recorrer todos los elementos de ella para localizar uno en particular.

Formalmente se define un árbol binario de búsqueda de la siguiente manera:

“Para todo nodo T del árbol debe cumplirse que todos los valores de los nodos del subárbol
izquierdo de T deben ser menores al valor del nodo T. De forma similar, todos los valores de los nodos
del subárbol derecho de T deben ser mayores al valor de nodo T”.

Algoritmos de búsqueda.

El siguiente algoritmo localiza un nodo en un árbol binario de búsqueda.

Raíz es una variable de tipo puntero que apunta a la raíz del árbol.

Dato es una variable de tipo entero que contiene la información que se desea localizar en el
árbol. Cabe aclarar que en la primera llamada la variable Raíz no puede ser nula.

41
Carlos A. Fernández
Estructura de Datos

Búsqueda (Raíz, Dato)


Inicio
Si (Dato < info(Raíz))
Si (izquierdo(Raíz) = nulo)
Escribir “El nodo no se encuentra en el árbol”
si no
Búsqueda (izquierdo(Raíz), Dato))
fin_si
si no
Si (Dato > info(Raíz))
Si (derecho(Raíz) = nulo)
Escribir “ El nodo no se encuentra en el árbol”
si no
Búsqueda (derecho(Raíz, Dato))
fin_si
si no
Escribir “El nodo se encuentra en el árbol”
fin_si
fin_si
Fin

Otra variante del algoritmo de búsqueda es el siguiente:

Búsqueda1 (Raíz, Dato)


Inicio
Si Raíz ≠ nulo
Si (Dato < info(Raíz))
Búsqueda1 (izquierdo(Raíz), Dato))
si no
Si (Dato > info(Raíz))
Búsqueda1 (derecho(Raíz, Dato))
si no
Escribir “El nodo se encuentra en el árbol”
fin_si
fin_si
si no
Escribir “El nodo no se encuentra en el árbol”
fin_si
Fin

42
Carlos A. Fernández
Estructura de Datos

Inserción en un árbol binario de búsqueda

La inserción es una operación que se puede realizar eficientemente en un árbol binario de


búsqueda. La estructura crece conforme se inserten elementos al árbol. Los pasos que deben realizarse
para insertar un elemento a un árbol binario de búsqueda son los siguientes:

1. Debe compararse la clave a insertar con la raíz del árbol. Si es mayor, debe avanzarse hacia el
subárbol derecho. Si es menor, debe avanzarse hacia el subárbol izquierdo.
2.
3. Repetir sucesivamente el paso 1 hasta que se cumpla alguna de las siguientes condiciones:
2.1. El subárbol derecho es igual o vacío, o el subárbol izquierdo es igual a vacío; en cuyo caso se
procederá a insertar el elemento en el lugar que le corresponde.
2.2. La clave que quiere insertarse es igual a la raíz del árbol; en cuyo caso no se realiza la
inserción.

El siguiente algoritmo realiza la inserción de un nodo en un árbol binario de búsqueda. Raíz es


una variable de tipo puntero y la primera vez debe ser distinta de vacío. Dato es una variable de tipo
entero que contiene la información del elemento que se quiere insertar. Se utiliza además, como
auxiliar, la variable otro de tipo puntero.
Inserción (raíz, dato)
Inicio
Si dato < getInfo(raíz)
Si getIzquierdo(raíz) = nulo
generaNodo(otro)
setIzquierdo(otro, nulo)
setDerecho(otro, nulo)
setInfo(otro, dato)
setIzquierdo(raíz, otro)
si no
Inserción (getIzquierdo(raíz), dato)
si no
Si dato >getIinfo(raíz)
Si getDerecho(raíz) = nulo
generaNodo(otro)
setIzquierdo(otro, nulo)
setDerecho(otro, nulo)
setInfo(otro, dato)
setDerecho(raíz, otro)
si no
Inserción (getDerecho(raíz), dato)
fin_si
si no
Escribir “El nodo ya se encuentra en el árbol”
fin_si
fin_si
Fin

43
Carlos A. Fernández
Estructura de Datos

Otra forma de expresar el algoritmo de inserción es la siguiente:

Inserción1(raíz, dato)
Inicio
Si Raíz ≠ nulo
Si dato <getInfo(raíz)
Inserción1 (getIzquierdo(raíz), dato)
si no
Si Dato > getInfo(raíz)
Inserción1 (getDerecho(raíz), dato)
si no
Escribir “El nodo ya se encuentra en el árbol”
fin_si
fin_si
si no
generaNodo(otro)
setIzquierdo(otro, nulo)
setDerecho(otro, nulo)
setIinfo(otro, dato)
raíz ← otro
fin_si
Fin

44
Carlos A. Fernández
Estructura de Datos

Borrado en un árbol binario de búsqueda

La operación de borrado es un poco más complicada que la de inserción.

Ésta consiste en eliminar un nodo del árbol sin violar los principios que definen justamente
un árbol binario de búsqueda. Se debe distinguir los siguientes casos:

1. Si el elemento a borrar es terminal u hoja, simplemente se suprime.

2. Si el elemento a borrar tiene un solo descendiente, entonces tiene que sustituirse por ese
descendiente.

3. Si el elemento a borrar tiene los dos descendientes, entonces se tiene que sustituir por el nodo que
se encuentra más a la izquierda en el subárbol derecho o por el nodo que se encuentra más a la
derecha en el subárbol izquierdo.

Además, debemos recordar que antes de eliminar un nodo, debe localizársele en el árbol.
Para esto, se utilizará el algoritmo de búsqueda presentado anteriormente.

El siguiente algoritmo realiza la eliminación de un elemento en un árbol binario de búsqueda.

Raíz es una variable de tipo puntero (por referencia).


Dato es un variable de tipo entero que contiene la información del nodo que se desea eliminar.
Además Aux, Aux1 y Otro son variables auxiliares de tipo puntero.

45
Carlos A. Fernández
Estructura de Datos

Eliminación (Raíz, Dato)


Inicio
Si Raíz ≠ nulo
Si Dato < getInfo(Raíz)
Eliminación (getIzquierdo(Raíz), Dato)
si no
Si Dato > getInfo(Raíz)
Eliminación (getDerecho(Raíz), Dato)
si no
Otro ← Raíz
Si getDerecho(Otro) = nulo
Raíz ← getIzquierdo(Otro)
si no
Si getIzquierdo(Otro) = nulo
Raíz ← getDerecho(Otro)
si no
Aux ← getIzquierdo(Otro)
Aux1 ← Aux
Mientras getDerecho(Aux) ≠ nulo
Aux1 ← Aux
Aux ← getDerecho(Aux)
fin_mientras
setInfo(Otro, getInfo(Aux) )
Otro ← Aux
setDerecho(Aux1, getIzquierdo(Aux))
fin_si
fin_si
fin_si
fin_si
liberaNodo(Otro)
si no
Escribir “El nodo no se encuentra en el árbol”
fin_si
Fin

46
Carlos A. Fernández
Estructura de Datos

Árboles balanceados
Cuando un árbol binario crece o decrece descontroladamente, el rendimiento puede disminuir
considerablemente. El caso más desfavorable se produce cuando se inserta un conjunto de claves
ordenadas en forma ascendente o descendente.

El número promedio de comparaciones que debe realizarse para localizar una determinada clave, en un
árbol binario de búsqueda con crecimiento descontrolado es N/2, cifra que muestra un rendimiento muy
pobre en la estructura.

Con el objeto de mejorar el rendimiento en la búsqueda surgen los árboles balanceados. La idea
central de éstos es la de realizar reacomodos o balanceos, después de inserciones o eliminaciones de
elementos. Estos árboles también reciben el nombre de AVL en honor a sus inventores, dos
matemáticos rusos, G.M. Adelson-Velskii y E.M. Landis.

Formalmente se define un árbol balanceado como un árbol binario de búsqueda, en el cual se debe
cumplir la siguiente condición: “Para todo nodo T del árbol, la altura de los subárboles izquierdo y
derecho no debe diferir en más de una unidad”.

Los árboles balanceados se parecen mucho, en su mecanismo de formación, a los números


Fibonacci. El árbol de altura 0 es vacío, el árbols de altura 1 tiene un único nodo y en general el
número de nodos del árbol con altura h>1 se calcula aplicando la siguiente fórmula recursiva:

Kh = Kh-1 + 1 + Kh-2

donde K indica el número de nodos del árbol y h la altura.

Algunos estudios demuestran que la altura de un árbol balanceado de n nodos nunca excederá de
1.44*log n.

47
Carlos A. Fernández
Estructura de Datos

Al insertar un elemento en un árbol balanceado deben distinguirse los siguientes casos:

1. Las ramas izquierdas (RI) y derecha (RD) del árbol tienen la misma altura (HRI = HRD), por lo tanto:
2|.1. Si se inserta un elemento en RI entonces HRI será mayor a HRD.
2|.2. Si se inserta un elemento en RD entonces HRD será mayor a HRI.

Obsérvese que en cualquiera de los dos casos mencionados (1.1 y 1.2), no se viola el criterio de
equilibrio del árbol.

1. Las ramas izquierda (RI) y derecha (RD) del árbol tienen altura diferente (HRI ≠ HRD):
2|.1. Supóngase que HRI < HRD:
2.1.1.Si se inserta un elemento en RI entonces HRI será igual a HRD.
2.1.2.{Las ramas tienen ala misma altura, por lo que se mejora el equilibrio del árbol}
2.1.3.Si se inserta un elemento en RD entonces se rompe el criterio de equilibrio del árbol y es
necesario restructurarlo.
2|.1. Supóngase que HRI > HRD:
2.1.1.Si se inserta un elemento en RI, entonces se rompe el criterio de equilibrio del árbol y es
necesario restructurarlo.
2.1.2.Si se inserta un elemento en RD, entonces HRD será igual a HRI.
2.1.3.{Las ramas tienen la misma altura, por lo que se mejora el equilibrio del árbol}

Para poder determinar si un árbol está balanceado o no, debe manejarse información relativa al
equilibrio de cada nodo del árbol. Surge así el concepto de factor de equilibrio de un nodo (FE) que
se define como: la altura del subárbol derecho menos la altura del subárbol izquierdo.

FE = HRD – HRI

Los valores que puede tomar FE son: -1, 0, 1. Si FE llegara a tomar los valores de –2 o 2, entonces
debería restructurarse el árbol.

A continuación se presenta la definición de un árbol balanceado en lenguaje C.

struct NodoArbol {
int dato;
int FE;
struct NodoArbol *aptIzq, *aptDer;
};

RESTRUCTURACIÓN DEL ÁRBOL BALANCEADO

El proceso de inserción en un árbol balanceado es sencillo pero con algunos detalles un


poco complicados. Primero debe seguirse el camino de búsqueda del árbol, hasta
localizar el lugar donde hay que insertar el elemento. Luego se calcula su FE, que
obviamente será 0, y regresamos por el camino de búsqueda calculando el FE de los
48
Carlos A. Fernández
Estructura de Datos

distintos nodos. Si en alguno de los nodos se viola el criterio de equilibrio entonces


debe restructurarse el árbol. El proceso termina al llegar a la raíz del árbol, o cuando se
realiza la restructuración del mismo; en cuyo caso no es necesario determinar el FE de
los restantes nodos.
Restructurar el árbol significa rotar los nodos del mismo. La rotación puede ser simple o
compuesta. El primer caso involucra dos nodos y el segundo caso afecta a 3. Si la
rotación es simple puede realizarse por las ramas derechas (DD) o por las ramas
izquierdas (II). Si la rotación es compuesta puede realizarse por las ramas derecha e
izquierda (DI) o por las ramas izquierda y derecha (ID).

DD
SIMPLE
II
ROTACIÓN
DI
COMPUESTA
ID

El algoritmo inserta un elemento en un árbol balanceado. Raíz es una variable de tipo puntero (por
referencia). Bo es una variable de tipo booleano (por referencia). Bo se utiliza para indicar que la altura
del árbol ha crecido, su valor inicial es FALSO. Dato es una variable de tipo entero que contiene la
información del elemento que queremos insertar. Otro, Nodo1 y Nodo2 son variables auxiliares de tipo
puntero.
InsertaBalanceado (Raíz, Bo, Dato)
Inicio
Si Raíz ≠ nulo
entonces
Si Dato < info(Raíz)
entonces
InsertaBalanceado (izquierdo(Raíz), Bo, Dato)
Si Bo = VERDADERO
entonces
Según FE(Raíz)
= 1 : FE(Raíz) ← 0
Bo ← FALSO
= 0 : FE(Raíz) ← -1
=-1 : {Reestructuración del árbol}
Nodo1 ← izquierdo(Raíz)
Si FE(Nodo1) ≤ 0
entonces { Rotación II }
izquierdo(Raíz) ← derecho(Nodo1)
derecho(Nodo1) ← Raíz
FE(Raíz) ← 0
49
Carlos A. Fernández
Estructura de Datos

El pseudocódigo de búsqueda binaria para localizar un valor en un arreglo ordenado


ascendentemente V, de N elementos, sería:

inicio
Introducir VALOR
POS_INI ← 1
POS_FIN ← N
POS_MED ← ENT( (POS_INI + POS_FIN) / 2)
mientras (VALOR ≠ V[POS_MED]) y POS_FIN > POS_INI)
si VALOR > V[POS_MED]
entonces
POS_INI ← POS_MED + 1
si no
POS_FIN ← POS_MED – 1
fin_si
POS_MED ← ENT( (POS_INI + POS_FIN) / 2)
fin_mientras
si VALOR = V[POS_MED]
entonces
Imprimir “Posición: “, POS_MED
si no
Imprimir “No existe en el arreglo.”
fin_si
fin

58
Carlos A. Fernández
Estructura de Datos

Búsqueda por transformación de claves (Hash)

Este método, llamado por transformación de claves, permite aumentar la velocidad de


búsqueda sin necesidad de tener los elementos ordenados. Cuenta también con la ventaja de que el
tiempo de búsqueda es prácticamente independiente del número de componentes del arreglo.

Este método trabaja basándose en una función de transformación o función hash (h) que
convierte una clave dada en una dirección (índice) dentro del arreglo.

dirección ← H(clave)

La función hash es aplicada a la clave da un índice del arreglo, lo que permite accesar
directamente sus elementos.

Siempre debe equilibrarse el costo por espacio de memoria con el costo por tiempo de búsqueda.

Cuando se tienen claves que no se corresponden con índices (por ejemplo, por ser
alfanuméricas), o bien cuando las claves son valores numéricos muy grandes, debe utilizarse una
función hash que permita transformar la clave para obtener una dirección apropiada.

Esta función hash debe ser simple de calcular y debe asignar direcciones de la manera más
uniforme posible. Es decir, dadas dos claves diferentes debe generar posiciones diferentes. Si esto no
ocurre (H(K1) = d, H(K2) = d y K1 ≠ K2), hay una colisión.

Se define, entonces, una colisión como la asignación de una misma dirección a dos o más claves
distintas.

Para trabajar con este método de búsqueda debe elegirse previamente:

• Una función hash que sea fácil de calcular y que distribuya uniformemente las claves.

• Un método para resolver colisiones. Si éstas se presentan se debe contar con algún método que
genere posiciones alternativas.

Funciones hash

No hay reglas que permitan determinar cuál será la función más apropiada para un conjunto de
claves, de tal manera que asegure la máxima uniformidad en la distribución de las mismas.

1. Función módulo (por división)

Consiste en tomar el residuo de la división de la clave entre el número de componentes del


arreglo. Supóngase que se tiene un arreglo de N elementos, ya sea K la clave del dato a buscar. La
función hash queda definida por la siguiente fórmula:

H(k) = (K mod N) + 1

59
Carlos A. Fernández
Estructura de Datos

En la fórmula puede observarse que al residuo de la división se le suma 1, esto es para obtener
un valor entre 1 y N.

Para lograr una mayor uniformidad en la distribución, N debe ser un número primo o divisible
por muy pocos números. Por lo tanto, dado N, si éste no es un número primo se tomará el valor primo
más cercano.

Ejemplo:

Sean N = 100 el tamaño del arreglo, y sean sus direcciones los números entre 1 y 100. Sean K1 =
7259 y K2 = 9359 dos claves a las que deban asignarse posiciones en el arreglo. Se aplica la fórmula
con N = 100, para calcular las direcciones correspondientes a K1 y K2.

H(K1) = (7259 mod 100) + 1 = 60

H(K2) = (9359 mod 100) + 1 = 60

Como H(K1) = H(K2) y K1 ≠ K2, se está ante una colisión.

Se aplica ahora la fórmula con N igual a un valor primo en vez de utilizar N = 100.

H(K1) = (7259 mod 97) + 1 = 82

H(K2) = (9359 mod 97) + 1 = 48

Con N = 97 se ha eliminado la colisión.

1. Función cuadrado

Consiste en elevar al cuadrado la clave y tomar los dígitos centrales como dirección. El número
de dígitos a tomar queda determinado por el rango del índice. Sea K la clave del dato a buscar. La
función hash queda definida por la siguiente fórmula:

H(K) = dígitos_centrales (K2) + 1

La suma de una unidad a los dígitos centrales es para obtener un valor entre 1 y N.

Ejemplo:

Sean N = 100 el tamaño del arreglo, y sean sus direcciones los números entre 1 y 100. Sean K1 =
7259 y K2 = 9359 dos claves a las que deban asignarse posiciones en el arreglo. Se aplica la fórmula
para calcular las direcciones correspondientes a K1 y K2.

K12 = 52 693 081

K22 = 87 590 881


60
Carlos A. Fernández
Estructura de Datos

H(K1) = dígitos_centrales (52 693 081) + 1 = 94

H(K2) = dígitos_centrales (87 590 881) + 1 = 91

Como el rango de índices en el arreglo varía de 1 a 100 se toman solamente los dos dígitos
centrales del cuadrado de las claves.

1. Función plegamiento

Consiste en dividir la clave en partes de igual número de dígitos (la última puede tener menos
dígitos) y operar con ellas, tomando como dirección los dígitos menos significativos. La operación
entre las partes puede hacerse por medio de sumas o multiplicaciones. Sea K la clave del dato a buscar.
K está formada por los dígitos d1, d2, …, dn. La función hash queda definida por la siguiente fórmula:

H(K) = dígmensig ((d1 … di) + (di … dj) + ... + (d1 … dn)) + 1


ó
H(K) = dígmensig ((d1 … di) * (di … dj) * ... * (d1 … dn)) + 1

El operador que aparece en la fórmula operando las partes de la clave es el de suma. La suma de
una unidad a los dígitos menos significativos (dígmensig) es para obtener un valor entre 1 y N.

Ejemplo:

Sean N = 100 el tamaño del arreglo, y sean sus direcciones los números entre 1 y 100. Sean K1 =
7259 y K2 = 9359 dos claves a las que deban asignarse posiciones en el arreglo. Se aplica la fórmula
para calcular las direcciones correspondientes a K1 y K2.

H(K1) = dígmensig (72 + 59) + 1 = dígmensig (131) + 1 = 32

H(K2) = dígmensig (93 + 59) + 1 = dígmensig (152) + 1 = 53

De la suma de las partes se toman solamente dos dígitos porque los índices del arreglo varían de
1 a 100.

1. Función truncamiento

Consiste en tomar algunos dígitos de la clave y formar con ellos una dirección. Este método es
de los más sencillos, pero es también de los que ofrecen menos uniformidad en la distribución de las
claves.

Sea K la clave del dato a buscar. K está formada por los dígitos dígitos d1, d2, …, dn. La función
hash queda definida por la siguiente fórmula:

H(K) = elegirdígitos (d1, d2, …, dn.) + 1

61
Carlos A. Fernández
Estructura de Datos

La elección de los dígitos es arbitraria. Podrían tomarse los dígitos de las posiciones impares o
de las pares. Luego podría unírseles de izquierda a derecha o de derecha a izquierda. La suma de una
unidad a los dígitos seleccionados es para obtener un valor entre 1 y 100.

Ejemplo:

Sean N = 100 el tamaño del arreglo, y sean sus direcciones los números entre 1 y 100. Sean K1 =
7259 y K2 = 9359 dos claves a las que deban asignarse posiciones en el arreglo. Se aplica la fórmula
para calcular las direcciones correspondientes a K1 y K2.

H(K1) = elegirdígitos (7 2 5 9) + 1 = 76

H(K2) = elegirdígitos (9 3 5 9) + 1 = 96

En este ejemplo se toma el primer y tercer número de la clave y se une éste de izquierda a
derecha.

En todos los casos anteriores se presentan ejemplos de claves numéricas. Sin embargo, en la
realidad las claves pueden ser alfabéticas o alfanuméricas. En general, cuando aparecen letras en las
claves se suele asociar a cada una un entero a efectos de convertirlas en numéricas.

Si la clave fuera ADA, su equivalente numérico sería 010401. Si hubiera combinación de letras
y números, se procedería de la misma manera. Por ejemplo, para la clave Z4F21, su equivalente
numérico sería 2740621.

Otra alternativa sería, para cada carácter, tomar su valor decimal asociado según el código
ASCII. Una vez obtenida la clave en su forma numérica, se puede utilizar normalmente cualquiera de
las funciones arriba mencionadas.

Solución de colisiones.

La elección de método adecuado para resolver colisiones es tan importante como la elección de
una buena función hash. Normalmente, cualquiera que sea el método elegido, resulta costoso tratar las
colisiones. Es por ello que debe hacerse un esfuerzo por encontrar la función que ofrezca mayor
uniformidad en la distribución de las claves.

La manera más natural de resolver el problema de las colisiones es reservar una casilla por
clave. Es decir, que aquellas se correspondan una a una con las posiciones del arreglo. Pero como ya se
mencionó, esta solución puede tener un alto costo en memoria. Por lo tanto deben analizarse otras
alternativas que permitan equilibrar el uso de memoria con el tiempo de búsqueda.

Reasignación

Existen varios métodos que trabajan bajo el principio de comparación y reasignación de


elementos. Se analizarán tres de ellos:

62
Carlos A. Fernández
Estructura de Datos

• Prueba lineal
• Prueba cuadrática
• Doble dirección hash

a) Prueba lineal

Consiste en que una vez detectada la colisión se debe recorrer el arreglo secuencialmente a
partir del punto de colisión, buscando al elemento. El proceso de búsqueda concluye cuando el
elemento es hallado, o bien cuando se encuentra una posición vacía. Se trata al arreglo como a una
estructura circular: el siguiente elemento después del último es el primero.

PRUEBALINEAL (V, N, K)
Inicio
D ← H(K) {Genera dirección}
Si V[D] = K
entonces
Escribir “El elemento está en la posición”, D
si no
DX ← D + 1
mientras (DX ≤ N) y (V[DX] ≠ K) y (V[DX] ≠ VACÍO) y (DX ≠ D)
DX ← DX + 1
Si DX = N + 1
entonces
DX ← 1
fin_si
fin_mientras
Si V[DX] = K
entonces
Escribir “El elemento está en la posición”, DX
si no
Escribir “El elemento no está en el arreglo”
fin_si
fin_si
Fin

La cuarta condición del ciclo mientras (DX ≠ D), es para evitar caer en un ciclo infinito, si el
arreglo estuviera lleno y el elemento a buscar no se encontrara en él.

La principal desventaja de este método es que puede haber un fuerte agrupamiento alrededor
de ciertas claves, mientras que otras zonas del arreglo permanecerían vacías. Si las concentraciones de
claves son muy frecuentes, la búsqueda será principalmente secuencial perdiendo así las ventajas del
método hash.

a) Prueba cuadrática
63
Carlos A. Fernández
Estructura de Datos

Este método es similar al de la prueba lineal. La diferencia consiste en que en el cuadrático las
direcciones alternativas se generarán como D + 1, D + 4, D + 9, …, D + i2 en vez de D + 1, D + 2, …,
D + i. Esta variación permite una mejor distribución de las claves colisionadas.

PRUEBACUADRÁTICA (V, N, K)
Inicio
D ← H(K) {Genera dirección}
Si V[D] = K
entonces
Escribir “El elemento está en la posición”, D
si no
I←1
DX ← D + I2
Mientras (V[DX] ≠ K) y (V[DX] ≠ VACÍO)
I←I+1
DX ← D + I2
Si DX > N
entonces
I←0
DX ← 1
D←1
fin_si
fin_mientras
Si V[DX] = K
entonces
Escribir “El elemento está en la posición”, DX
si no
Escribir “El elemento no está en el arreglo”
fin_si
fin_si
Fin

La principal desventaja de este método es que pueden quedar casillas del arreglo sin visitar.
Además, como los valores de las direcciones varían en I2 unidades, resulta difícil determinar una
condición general para detener el ciclo mientras. Este problema podría solucionarse empleando una
variable auxiliar, cuyos valores dirijan el recorrido del arreglo de tal manera que garantice que serán
visitadas todas las casillas.

a) Doble dirección hash

Consiste en que una vez detectada la colisión se debe generar otra dirección aplicando la
función hash a la dirección previamente obtenida. El proceso se detiene cuando el elemento es hallado,
o bien cuando se encuentra una posición vacía.

D H(K)
D’ H(D)
64
Carlos A. Fernández
Estructura de Datos

D’’ H(D’)

La función hash que se aplique a las direcciones puede o no ser la misma que originalmente se
aplicó a la clave. No existe una regla que permita decidir cuál será la mejor función a emplear en el
cálculo de las sucesivas direcciones.

DOBLEDIRECCIÓN (V, N, K)
Inicio
D ← H(K) {Genera dirección}
Si V[D] = K
entonces
Escribir “El elemento está en la posición”, D
si no
DX ← H’(D)
Mientras (DX ≤ N) y (V[DX] ≠ K) y (V[DX] ≠ VACÍO) y (DX ≠ D)
DX ← H’(DX)
fin_mientras
Si V[DX] = K
entonces
Escribir “El elemento está en la posición”, DX
si no
Escribir “El elemento no está en el arreglo”
fin_si
fin_si
Fin

d) Arreglos anidados

Este método consiste en que cada elemento del arreglo tenga otro arreglo en el cual se
almacenen los elementos colisionados. Si bien la solución parece ser sencilla, es claro también que
resulta ineficiente. Al trabajar con arreglos se depende del espacio que se haya asignado a éste. Lo cual
conduce a un nuevo problema difícil de solucionar: elegir un tamaño adecuado de arreglo que permita
un equilibrio entre el costo de memoria y el número de valores colisionados que pudiera almacenar.

e) Encadenamiento

Consiste en que cada elemento del arreglo tenga un apuntador a una lista ligada, la cual se irá
generando e irá almacenando los valores colisionados a medida que se requiera. Es el método más
eficiente debido al dinamismo propio de las listas. Cualquiera que sea el número de colisiones
registradas en una posición, siempre será posible tratar una más. Como desventajas del método de
encadenamiento se cita el hecho de que ocupa espacio adicional al de la tabla, y que exige el manejo de
listas ligadas. Además, si las listas crecen demasiado se perderá la facilidad de acceso directo del
método hash.

Ejercicios
65
Carlos A. Fernández
Estructura de Datos

1. Escriba un programa para búsqueda secuencial en un arreglo desordenado, que obtenga todas las
ocurrencias de un dato dado.

1. Dado un arreglo que contiene los nombres de N alumnos ordenados alfabéticamente, escriba un
programa que encuentre un nombre dado en el arreglo. Si lo encuentra debe regresar como resultado
la posición en la que lo encontró. En caso contrario, debe enviar un mensaje adecuado.

1. Dado un arreglo de N componentes que contienen la siguiente información:

• Nombre del alumno


• Promedio
• Número de materias aprobadas

Escriba un programa que lea el nombre de un alumno y regrese como resultado el promedio y el
número de materias aprobadas por dicho alumno. Si el nombre dado no está en el arreglo, envié un
mensaje adecuado.

a) Considere que el arreglo está desordenado


b) Considere que el arreglo está ordenado

1. Escriba un programa para búsqueda secuencial en arreglos ordenados de manera descendente.

1. Escriba un programa para búsqueda secuencial en listas enlazadas desordenadas. Si el elemento se


encuentre en la lista, indique el número de nodo en el cual se encontró. En caso contrario, emita un
mensaje adecuado.

1. Escriba un programa para búsqueda secuencial en listas enlazadas ordenadas de manera


descendente.

1. Escriba un programa de búsqueda binaria en arreglos ordenados.

a) De manera ascendente
b) De manera descendente

1. Resuelva el inciso b del problema 3 utilizando el algoritmo de búsqueda binaria.

1. Dado que se quiere almacenar los registros con claves: 23, 42, 5, 66, 14, 43, 59, 81, 37, 49, 28, 55,
94, 80 y 64 en un arreglo de 20 elementos, defina una función hash que distribuya los registros en el
arreglo. Si hubiera colisiones, resuélvalas aplicando el método de reasignación lineal.

1. De un grupo de N alumnos se tienen los siguientes datos:

• Matrícula: valor entero comprendido entre 1000 y 4999


• Nombre: cadena de caracteres
• Dirección: cadena de caracteres

El campo clave es matrícula.


66
Carlos A. Fernández
Estructura de Datos

Los N registros han sido almacenados en un arreglo, aplicando la siguiente función hash:

H(clave) = dígitos_centrales (clave2) + 1

Las colisiones han sido tratadas con el método de doble dirección hash.

Escriba un subprograma que lea la matrícula de un alumno y regrese como resultado el nombre y
dirección del mismo. En caso de no encontrarlo emita un mensaje adecuado.

1. Se quiere almacenar en un arreglo los siguientes datos de N personas:

• Clave de contribuyente: alfanumérico, de longitud 6.


• Nombre: cadena de caracteres
• Dirección: cadena de caracteres
• Saldo: real

Defina una función hash para almacenar en un arreglo los datos mencionados. Utilice el método de
encadenamiento para resolver las colisiones.

1. Presente y explique una función hash que permita almacenar en un arreglo los elementos de la tabla
periódica de los elementos químicos y sus propiedades, de una manera uniforme. La clave está dada
por el nombre de los elementos.

1. Utilice la función definida en el ejercicio anterior para insertar y eliminar los elementos que se dan
a continuación:

Insertar: sodio, oro, osmio, litio, boro, cobre, plata, radio.


Eliminar: oro, osmio, boro, cobre, plata.

1. Dados los 12 signos del zodiaco (capricornio, acuario, piscis, aries, tauro, géminis, cáncer, leo,
virgo, libra, escorpión, sagitario).

a) Escriba un subprograma para almacenarlos en una estructura tries.


b) Escriba un subprograma de búsqueda para los signos, almacenados según lo especificado en el
inciso anterior.

67
Carlos A. Fernández

También podría gustarte