You are on page 1of 14

Universidad

Nacional
de Trujillo

EST
RUC
TUR

DOCENTE:
Cruz Florin Iris
FACULTAD:
Ciencias Fsicas y
Matemticas

ESCUELA:
Ing. Informtica II
Ciclo B
INTEGRANTES:
Moreno Cabrera Antony
RBOLES AVL
Kevin

ique Urbina Carmen

rboles AVL
Un rbol AVL es un rbol binario de bsqueda que cumple con la condicin de que la diferencia
entre las alturas de los subrboles de cada uno de sus nodos es, como mucho 1.
La denominacin de rbol AVL viene dada por los creadores de tal estructura (Adelson-Velskii y
Landis).
Recordamos que un rbol binario de bsqueda es un rbol binario en el cual cada nodo cumple
con que todos los nodos de su subrbol izquierdo son menores que la raz y todos los nodos del
subrbol derecho son mayores que la raz.
Recordamos tambin que el tiempo de las operaciones sobre un rbol binario de bsqueda son
O(log n) promedio, pero el peor caso es O(n), donde n es el nmero de elementos.
La propiedad de equilibrio que debe cumplir un rbol para ser AVL asegura que la profundidad
del rbol sea O(log(n)), por lo que las operaciones sobre estas estructuras no debern recorrer
mucho para hallar el elemento deseado. Como se ver, el tiempo de ejecucin de las
operacines sobre estos rboles es, a lo sumo O(log(n)) en el peor caso, donde n es la cantidad
de elementos del rbol.
Sin embargo, y como era de esperarse, esta misma propiedad de equilibrio de los rboles AVL
implica una dificultad a la hora de insertar o eliminar elementos: estas operaciones pueden no
conservar dicha propiedad.

rbol AVL de enteros

A modo de ejemplificar esta dificultad, supongamos que al rbol AVL de enteros le queremos
agregar el entero 3. Si lo hacemos con el procedimiento normal de insercin de rboles binarios
de bsqueda el resultado sera el rbol de la siguiente figura el cual ya no cumple con la
condicin de equilibrio de los rboles AVL dado que la altura del subrbol izquierdo es y la del
subrbol derecho es 0.

rbol que no cumple con la condicin


de equilibrio de los rboles AVL

FE

Estructura de Datos

rboles AVL

La condicin de equilibrio garantiza que todas las operaciones, se resuelvan


eficientemente.
Para saber cundo se ha perdido la propiedad AVL es necesario conocer el factor de
equilibrio (FE) de cada nodo afectado por la insercin o borrado.
Esto puede hacerse calculando la diferencia de altura de los subrboles para cada nodo:
(altura subrbol derecho) (altura subrbol izquierdo).
Al insertar un nuevo nodo el FE toma el valor cero ya que siempre se inserta por las
hojas.
Despus de cada insercin hay que revisar el FE de los nodos involucrados, que sern
los que componen el camino desde la raz del rbol hasta el padre del nodo insertado.

En un rbol AVL puede suceder que:


f.e. = 1 ==> h(der) > h(izq)
f.e. = 0 ==> h(der) = h(izq)
f.e. = -1 ==> h(der) < h(izq)
Un nodo est en desequilibrio cuando su FE > |1|.
A continuacin se lista la declaracin del tipo abstracto de dato rbol AVL:
1.
2.
3.
4.
5.
6.
7.
8.

typedef struct AVLNode AVLTree;


struct AVLNode
{
int dato; ..( 1 )
AVLTree izq;
AVLTree der;
int altura; (2)
};

(1) Como ya dijimos, por cuestiones de simplicidad, la informacin almacenada en cada


nodo del rbol ser un entero.
(2) Cada nodo tendr almacenada su propia altura con respecto a la raz absoluta del rbol
con el que estamos trabajando.
A continuacin declaramos las operaciones bsicas sobre rboles binarios y con las cuales
trabajaremos para acceder al tipo abstracto de dato rbol AVL de aqu en ms. Note: Si se usa
algn lenguaje orientado a objetos como C++ o java y ya se tienen clases como rboles binarios
o rboles binarios de bsqueda, conviene declarar los rboles AVL como una subclase de alguna
de estas. Luego, las operaciones declaradas a continuacin se heredarn de estos tipos.
// Constructores
AVLTree *vacio (void); // devuelve un rbol AVL vaco
AVLTree *hacer (int x, AVLTree * izq, AVLTree * der); //devuelve un nuevo rbol formado por
una raz con valor x, subrbol izquierdo el rbol izq y subrbol derecho el rbol der.
// Predicados
bool es_vacio (AVLTree * t); //devuelve true, si t es un rbol vaco.

Estructura de Datos

rboles AVL
// Selectores
AVLTree *izquierdo (AVLTree * t); //devuelve el subrbol izquierdo de t.
AVLTree *derecho (AVLTree * t); // devuelve el subrbol derecho de t.
int raiz (AVLTree * t); //devuelve el valor de la raz del rbol t. Precondicin: !es_vacio(t)
int altura (AVLTree * t); // devuelve la altura del nodo t en el rbol
// Destructores
void destruir (AVLTree * t, void (*free_dato) (int)); //libera la memoria ocupada por los nodos
del rbol. Si los datos almacenados en cada nodo estn almacenados dinmicamente y se los
quiere liberar tambin, debe pasarse como segundo parmetro una funcin de tipo void func(int
t) que libere la memoria de objetos int. Si los datos no estn almacenados dinmicamente o
simplemente no se los quiere destruir (liberar de memoria), psese como segundo parmetro
NULL. Nota: Funcin Recursiva
ALTURA DE UN NODO
Una funcin para calcular la altura de un nodo puede escribirse recursivamente como:
1.
2.
3.
4.
5.
6.
7.

int altura(AVLTree *t)


{
if(es_vacio(t))
return -1;
else
return max(altura(izquierdo(t)), altura(derecho(t)));
}

Queremos que la altura de un rbol que consta de slo un nodo sea 0. Entonces debemos definir
la altura de un rbol vaco como -1. Sin embargo, no podemos darnos el lujo de tener una
funcin cuyo tiempo de ejecucin siempre es O(n) ya que, como dijimos, necesitamos la altura
de un nodo en tiempo constante. Para ello, redefiniremos la funcin de la siguiente manera,
aprovechando el campo altura que ahora tiene cada nodo del rbol.
1.
2.
3.
4.
5.
6.
7.

int altura (AVLTree * t)


{
if(es_vacio(t))
return -1;
else
return t->altura;
}

Importante: Debemos tener mucho cuidado en actualizar el campo altura de cada nodo siempre
que modifiquemos de alguna manera el rbol AVL. As, es importante tener una funcin que nos
permita actualizar la altura de un nodo cualquiera del rbol y cuyo tiempo de ejecucin sea O(1)
en el peor de los casos. A continuacin se lista una tal funcin:
1. Void actualizar_altura (AVLTree * t)
2. {
3. if(!es_vacio(t))

Estructura de Datos

rboles AVL
4. t->altura = max (altura ((t)->izq), altura ((t)->der)) + 1;
5. }
OPERACIONES
Rotaciones
El reequilibrado se produce de abajo hacia arriba sobre los nodos en los que se produce el
desequilibrio. Pueden darse dos casos: rotacin simple o rotacin doble; a su vez ambos
casos pueden ser hacia la derecha o hacia la izquierda.

RSI
RSD
RDI
RDD

Insercin
Extraccin
Bsqueda
ROTACIONES SIMPLES
Veremos a continuacin una operacin sencilla sobre un rbol binario de bsqueda que conserva
el orden en sus nodos y que nos ayudar a restaurar la propiedad de equilibrio de un rbol AVL
al efectuar operaciones sobre el mismo que puedan perturbarla.

rbol antes de la rotacin simple


Miremos por un momento el rbol de la figura. Dado que este es un rbol de bsqueda se debe
cumplir x < y, y adems todos los nodos del subrbol A deben ser menores que x y y; todos los
nodos del subrbol B deben ser mayores que x pero menores que y; y todos los nodos del
subrbol C deben ser mayores que y y por lo tanto que x.
En la siguiente figura se ha modificado sencillamente el rbol. Como puede verificarse
fcilmente por las desigualdades descriptas en el prrafo anterior, el nuevo rbol sigue
manteniendo el orden entre sus nodos, es decir, sigue siendo un rbol binario de bsqueda. A
esta transformacin se le denomina rotacin simple (o sencilla).

Estructura de Datos

rboles AVL

rbol luego de la

rotacin simple

Veamos un ejemplo
concreto.
Deseamos
insertar el nmero 3
en el rbol de enteros de
Figura 1. La insercin se muestra punteada en Figura 2. Sin embargo, como puede verse, la
insercin a provocado la prdida de la propiedad de equilibrio del rbol ya que dicha propiedad
no se cumple en el nodo marcado con rojo. Qu hacemos para recomponer dicha propiedad?
Simplemente realizamos una rotacin simple. En este caso se dice que la rotacin es izquierda
ya que la "prdida de equilibrio se produce hacia la izquierda. En Figure 3 puede verse el rbol
luego de la rotacin: la propiedad de equilibrio ha sido restablecida. Como mostramos atrs, la
rotacin conserva el orden entre los nodos, por lo que podemos afirmar que este ltimo rbol si
es AVL.

Figura 1. rbol
Avl2. rbol luego de la insercin. Prdida de la propiedad de equilibrio marcada con rojo
Figura

Figura 3. Restablecimiento de la propiedad de equilibrio mediante una rotacin simple

Estructura de Datos

rboles AVL

Como podemos observar, el resultado luego de la rotacin es un rbol AVL: posee tanto el rden
correcto de un rbol de bsqueda entre sus nodos y la propiedad de equilibrio. En este caso el
"desequilibrio" en el rbol con raz 5 era enteramente hacia la izquierda y por lo tanto, como ya
dijimos, la rotacin efectuada se denomina rotacin simple izquierda.
En el caso de un "desequilibrio" hacia la derecha, la rotacin es anloga y se denomina rotacin
simple derecha. En la siguiente figura se ven dos rboles: el primero tiene un "desequilibrio
hacia la derecha" marcado en rojo y el segundo es el resultado de aplicar una rotacin simple
derecha.

IMPLEMENTACION DE LA ROTACIN SIMPLE


1. void rotar_s (AVLTree ** t, bool izq); ... ( 1 )
2. /* realiza una rotacin simple del rbol t el cual se pasa por referencia. La rotacin ser
izquierda si (izq==true) o ser derecha si (izq==false)
Nota: las alturas de t y sus subrboles sern actualizadas dentro de esta funcin
Precondicin: si (izq==true) ==> !es_vacio(izquierdo(t))
si (izq==false) ==> !es_vacio(derecho(t))
( 2 )

Estructura de Datos

rboles AVL
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
(1)

void rotar_s (AVLTree ** t, bool izq)


...... ( 3 )
{
AVLTree *t1;
if (izq) // rotacin izquierda
{
t1 = izquierdo (*t);
(*t)->izq = derecho (t1);
t1->der = *t;
}
else // rotacin derecha
{
t1 = derecho (*t);
(*t)->der = izquierdo (t1);
t1->izq = *t;
}
// actualizamos las alturas de ambos nodos modificados */
actualizar_altura (*t);
actualizar_altura (t1);
// asignamos nueva raz
*t = t1;
}
Declaracin de la funcin rotar_s(). Esta declaracin y el siguiente comentario deberan
ir en el archivo de cabecera, interfaz del tipo abstracto rbol AVL con el usuario.
(2) Un breve comentario que explica lo que hace la funcin, los parmetros que acepta y las
precondiciones que stos deben cumplir para que la funcin se ejecute correctamente.
(3) Como el programador de C ms experimentado puede ver, el paso a la funcin es el
paso por referencia clsico en C. Lo que se pasa no es un puntero a la raiz del rbol sino
la direccin de dicho puntero. De esta manera, dentro de la funcin podremos cambiar
la misma raz si es necesario (lo que justamente hacemos en las rotaciones).
(4) Tambin se acepta como segundo parmetro un valor booleano que determina si la
rotacin simple a efectuar sobre el rbol es izquierda o derecha.
ROTACIONES DOBLES
Hemos visto cmo restaurar la propiedad de equilibrio cuando se presentan desequilibrios
"hacia la izquierda" o "hacia la derecha" luego de realizar inserciones en un rbol AVL. Sin
embargo y como veremos, pueden ocurrir "desequilibrios en zigzag", es decir desequilibrios que
no son ni a la derecha ni a la izquierda como es el caso de estos rboles:

Estructura de Datos

rboles AVL
En estos casos se aplica otro tipo de rotacin denominado rotacin doble la cual, anlogamente
a la rotacin simple, puede ser izquierda o derecha segn el caso. En realidad, la rotacin doble
constar de dos rotaciones simples.
El caso general de la rotacin doble izquierda en un rbol AVL se puede observar aqu:

La rotacin doble derecha es el proceso inverso a la rotacin doble izquierda. Dado que, como
vimos, una rotacin doble es en realidad dos rotaciones simples, podemos implementar la
funcin para la rotacin doble tan slo utilizando rotar_s vista anteriormente lo cual se hace a
continuacin:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.

void rotar_d (AVLTree ** t, bool izq);


...... ( 1 )
// realiza una rotacin doble. Funciona anlogamente a rotar_s().
void rotar_d (AVLTree ** t, bool izq)
.. ( 2 )
{
if (izq) // rotacin izquierda
{
rotar_s (&(*t)->izq, false);
rotar_s (t, true);
}
else // rotacin derecha
{

Estructura de Datos

rboles AVL
12.
rotar_s (&(*t)->der, true);
13.
rotar_s (t, false);
14.
}
15.
// la actualizacin de las alturas se realiza en las rotaciones simples
16. }
(1) Declaracin de la funcin rotar_d(); debera ir en un archivo de cabecera.
(2) Los parmetros de rotar_d() son anlogos a los de rotar_s()
BALANCE DEL ARBOL
Como se mostr anteriormente, cada vez que se modifique el rbol ( agreguen o eliminen
elementos) corremos el riesgo de que pierda su propiedad de equilibrio en alguno de sus nodos,
la cual debe conservarse si queremos obtener tiempos de ejecucin de orden O(log(n)) en el
peor de los casos.
La idea general que se utiliza en esta implementacin de rboles AVL para implementar los
algoritmos de insercin y de eliminacin de nodos sobre un AVL es la siguiente:
Efectuar los algoritmos de igual forma que en los rboles binarios de bsqueda pero
En cada recursin ir actualizando las alturas y rebalanceando el rbol en caso de que sea
necesario.
Lo que nos falta es una funcin que detecte un "desequilibrio" en un nodo dado del rbol y por
medio de un nmero finito de rotaciones lo equilibre.
En las secciones anteriores hemos ya descripto a grandes rasgos cul rotacin usar en cada caso
de desequilibrio. Esperamos que en el cdigo siguiente el lector pueda formalizar tales ideas.

1. void balancear (AVLTree ** t); ( 1 )


2. // Detecta y corrige por medio de un nmero finito de rotaciones un desequilibrio en el
rbol *t. Dicho desequilibrio no debe tener una diferencia de alturas de ms de 2
3. void balancear (AVLTree ** t)
4. {
5.
if(!es_vacio(*t))
6.
{
7.
if (altura (izquierdo (*t)) - altura (derecho (*t)) == 2)
( 2 )
8.
{ // desequilibrio hacia la izquierda
9.
if (altura ((*t)->izq->izq) >= altura ((*t)->izq->der))
.. ( 3 )
10.
//desequilibrio simple hacia la izquierda
11.
rotar_s (t, true);
12.
else
13.
//desequilibrio doble hacia la izquierda
14.
rotar_d (t, true);
15.
}
16.
else if (altura (derecho (*t)) - altura (izquierdo (*t)) == 2) .. ( 4 )
17.
{ // desequilibrio hacia la derecha!
18.
if (altura ((*t)->der->der) >= altura ((*t)->der->izq))
19.
//desequilibrio simple hacia la izquierda */
20.
rotar_s (t, false);
21.
else

Estructura de Datos

10

rboles AVL
22.
// desequilibrio doble hacia la izquierda
23.
rotar_d (t, false);
24.
}
25.
}
26. }
(1) Declaracin de la funcin balancear(). Esta declaracin junto con el comentario que le
sigue deberan estar en un archivo de cabecera usado para la interfaz del tipoabstracto
de dato rbol avl con el usuario-programador.
(2) Como dice en el comentario de la funcin, slo se contemplarn aquellos desequilibrios
cuya diferencia entre alturas es hasta 2.
(3) Sabiendo que en el nodo al que apunta *t hay un desequilibrio hacia la izquierda (de
diferencia de alturas 2), debemos averiguar qu clase de rotacin aplicar. En la siguiente
figura se explica grficamente a dnde apuntan las variables de la funcin en un rbol
genrico.
Decidiendo qu clase de rotacin aplicar para solucionar desequilibrio en el nodo.
Como
verse en el
nos
decidimos
rotacin
izquierda
subrbol
pesado de
es
el
o por una
doble
si
el
ms
(*t)->izq es el derecho.

puede
cdigo,
por una
simple
si
el
ms
(*t)->izq
izquierdo
rotacin
izquierda
subrbol
pesado de

(4) Si detectamos un desequilibrio hacia la derecha, la toma de decisiones son anlogas a


las de un desequilibrio hacia la izquierda, las cuales ya explicamos.

INSERCION
Implementaremos la insercin de elementos en un rbol AVL de forma anloga a cmo lo
haramos para rboles binarios de bsqueda salvo que en cada recursin del algoritmo
verificaremos y corregiremos el equilibrio del rbol. Tambin es importante ir actualizando las
alturas de cada nodo en cada recursin dado que las rotaciones, insercines y eliminaciones
pueden modificarlas.
Dado que ya vimos funciones tanto para balancear un rbol y para actualizar la altura de un
nodo (ambas de tiempo de ejecucin constante), estamos listos para implementar el algoritmo
de insercin. Esperamos que sea intuitivo.

1. void insertar (AVLTree ** t, int x);


Estructura de Datos

11

rboles AVL
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.

// inserta x en el rbol en un tiempo O(log(n)) peor caso.


void
insertar (AVLTree ** t, int x)
{
if (es_vacio (*t))
*t = hacer (x, vacio (), vacio ()); //altura actualizada automticamente
else
{
if (x < raiz (*t))
insertar (&(*t)->izq, x);
else
insertar (&(*t)->der, x);
balancear (t);
actualizar_altura (*t);
}
}

ELIMINACION
La estrategia para disear el algoritmo de eliminacin sobre rboles AVL es la misma que para
la insercin: Se utiliza el mismo algoritmo que sobre rboles binarios de bsqueda, pero en cada
recursin se detectan y corrijen errores por medio de balancear() y se actualiza la altura del
nodo actual.
Recordamos un poco la idea del algoritmo de eliminacin sobre rboles binarios de bsqueda.
Primero se recorre el rbol para detectar el nodo a eliminar. Una vez hecho esto hay tres casos a
diferenciar por su complejidad:
Si dicho nodo es una hoja procedemos a eliminarlos de inmediato, sin ms.
Si dicho nodo tiene un slo hijo, el nodo puede eliminarse despus de ajustar un apuntador del
padre para saltar el nodo.

Eliminacin de un nodo (7) con un slo hijo.


Si dicho nodo tiene dos hijos el caso es un poco ms complicado. Lo que se estila hacer (y que
de hecho se hace en el algoritmo gracias a la funcin auxiliar eliminar_min() ) reemplazar el
nodo actual por el menor nodo de su subrbol derecho (y luego eliminar ste).
1. void eliminar (AVLTree ** t, int x);

Estructura de Datos

12

rboles AVL
2. //elimina x del rbol en un tiempo O(log(n)) peor caso. Precondicin: existe un nodo
con valor x en el rbolt.
3. int eliminar_min (AVLTree ** t);
4. // Funcin auxiliar a eliminar(). Elimina el menor nodo del rbol *t devolviendo su
contenido (el cual no se libera de memoria). Se actualizan las alturas de los nodos.
Precondicin: !es_vacio(*t)
5. void eliminar (AVLTree ** t, int x)
6. {
7.
AVLTree *aux;
8.
if (x < raiz (*t))
9.
eliminar (&(*t)->izq, x);
10. else if (x > raiz (*t))
11.
eliminar (&(*t)->der, x);
12.
else //coincidencia
13.
{
14.
if (es_vacio (izquierdo (*t)) && es_vacio (derecho (*t)))
15.
{//es una hoja
16.
free (*t);
17.
(*t) = vacio();
18.
}
19.
else if (es_vacio (izquierdo (*t)))
20.
{//subrbol izquierdo vacio
21.
aux = (*t);
22.
(*t) = (*t)->der;
23.
free (aux);
24.
}
25.
else if (es_vacio (derecho (*t)))
26.
{// subrbol derecho vacio
27.
aux = (*t);
28.
(*t) = (*t)->izq;
29.
free (aux);
30.
}
31.
else // caso ms complicado
32.
{
33.
(*t)->dato = eliminar_min (&(*t)->der);
34.
}
35.
}
36.
balancear (t);
37.
actualizar_altura (*t);
38. }
39.
40. int eliminar_min (AVLTree ** t)
41. {
42. if (es_vacio (*t))
43. {
44.
fprintf (stderr, "No se respeta precondicin de eliminar_min()\n");
45.
exit(0);
46.
}
47.
else
48. {
49.
if (!es_vacio (izquierdo (*t)))
50.
{
51.
int x = eliminar_min (&(*t)->izq);
52.
balancear (t);

Estructura de Datos

13

rboles AVL
53.
actualizar_altura (*t);
54.
return x;
55.
}
56.
else
57.
{
58.
AVLTree *aux = (*t);
59.
int x = raiz (aux);
60.
*t = derecho (*t);
61.
free (aux);
62.
balancear (t);
63.
actualizar_altura (*t);
64.
return x;
65.
}
66.
}
67. }

Estructura de Datos

14

You might also like