Está en la página 1de 20

Universidad Nacional

Autónoma de México
Facultad de Estudios Superiores
Aragón
Equipo 3

Estructura de datos:

Arboles AVL

Profesor: Sánchez Hernández Miguel Ángel

Integrantes:

Montañez Franco Francisco Daniel

Morales Balderrama Carlos Alberto

Morales Cervantes Andrés Alberto

Pérez Morales César Eduardo

Villafaña García Jocelyn


Índice
Introducción................................................................................................3
Objetivos.....................................................................................................3
Árboles AVL.........................................................................................................................................3
Rotaciones...........................................................................................................................................4
Rotación simple a la derecha......................................................................................................4
Rotación simple a la izquierda....................................................................................................5
Rotación doble a la derecha........................................................................................................5
Rotación doble a la izquierda......................................................................................................6
Métodos.......................................................................................................8
Insertar..................................................................................................................................................8
Eliminar...............................................................................................................................................14
Recorrer amplitud.............................................................................................................................17
Altura...................................................................................................................................................18

Conclusión................................................................................................19
Bibliografía................................................................................................20
Referencias...............................................................................................20

2
Introducción
A lo largo del curso hemos sido capaces de resolver problemas con diversas técnicas y
estructuras de datos que como su objetivo principal dicta, es encontrar la solución de estos de
la manera más sencilla y practica posible, disminuyendo en gran medida la complejidad y costo
de procesamiento que estos representarían sin el uso de métodos mejorados.
Pero aún cuando podemos hacer uso de estas herramientas, el costo de algunos
procedimientos se vuelve sumamente complejos, probando que tengamos que recurrir a
mejores y más avanzados procesos, el método que analizaremos a continuación data de los
años 60, en el cual, gracias a los trabajos de dos matemáticos soviéticos, se logró dar con un
algoritmo que es sumamente eficaz al momento de organizar información y poder acceder a
ella, estamos hablando de los árboles AVL.

Antes de partir con la propia definición de este medio veamos al árbol como una estructura
organizativa que nos permite acceder y organizar datos de diversa índole en estructuras cada
vez más sencillas y mejor ejecutables al momento de su práctica, ya que, al emplear un árbol
AVL, no solo somos capaces de desarrollar una estructura mejorada, sino también, un medio
que, por su propia naturaleza, nos permite tener la cantidad de datos que necesitemos de
manera ya organizada, debido a que gracias a su estructura, y a un elemento conocido como
factor de equilibrio, que es el medio por el cual siempre cada nodo se mantendrá estructurado
en orden, podemos ser capaces de siempre mantener la estructura correcta del árbol.

Partiendo del empleo y la forma en la que operan, procederemos a definir un árbol, un árbol en
palabras de Chávez  “Un árbol es un tipo abstracto de datos (TAD) ampliamente usado que
imita la estructura jerárquica de un árbol, con un valor en la raíz y subárboles con un nodo
padre, representado como un conjunto de nodos enlazados”. Finalmente al conocer la
definición formal de un árbol de cualquier índole, procederemos a definir un árbol AVL, que en
palabras de sus creadores, fue definido en su publicación de un artículo en 1962, como: “ «An
algorithm for the organization of information» («Un algoritmo para la organización de la
información»).”

Teniendo nuestra definición teoría y su modo de ejecución, analizaremos la manera en la que


podemos construir e implementar arboles del tipo AVL.

Objetivos
- Comprender y analizar el funcionamiento de un árbol del tipo AVL.
- Construir los métodos que permiten la creación de un árbol AVL.
- Utilizar los métodos vistos en recursividad para el empleo de árboles AVL.
- Analizar las ventajas y desventajas que estos representan.
- Conocer el valor practico y los tipos de soluciones que ofrecen a problemáticas dadas.
Árboles AVL
Un árbol AVL es un tipo especial de árbol binario ideado por los matemáticos rusos Adelson-
Velskii y Landis. Fue el primer árbol de búsqueda binario auto-balanceable que se ideó.
El árbol AVL toma su nombre de las iniciales de los apellidos de sus inventores, Georgii
Adelson-Velskii y Yevgeniy Landis. Lo dieron a conocer en la publicación de un artículo en

3
1962, «An algorithm for the organization of information» («Un algoritmo para la organización de
la información»).
Los árboles AVL están siempre equilibrados de tal modo que, para todos los nodos, la altura de
la rama izquierda no difiere en más de una unidad de la altura de la rama derecha o viceversa.
Gracias a esta forma de equilibrio (o balanceo), la complejidad de una búsqueda en uno de
estos árboles se mantiene siempre en orden de complejidad O(log n). El factor de equilibrio
puede ser almacenado directamente en cada nodo o ser computado a partir de las alturas de
los subárboles.
Para conseguir esta propiedad de equilibrio, la inserción y el borrado de los nodos se ha de
realizar de una forma especial. Si al realizar una operación de inserción o borrado se rompe la
condición de equilibrio, hay que realizar una serie de rotaciones de los nodos.
Los árboles AVL más profundos son los árboles de Fibonacci.

Características y objetivos
El objetivo principal es resolver la operación de inserción en un Árbol AVL.
El objetivo secundario es reconocer qué tipo de rotación se debe aplicar en cada caso de
desbalanceo según los criterios propios de la estructura de datos.
 La propiedad de balanceo garantiza que la altura del árbol sea de O(log n).
 En cada nodo del árbol se guarda información de la altura.
 La altura del árbol vacío es -1. 
 Al realizar operaciones de inserción o eliminación se debe actualizar la información de
altura de los nodos y recuperar la propiedad de balanceo si fuera necesario, es decir, si
hubiera sido destruida.
Las operaciones básicas de un árbol AVL implican generalmente el realizar los mismos
algoritmos que serían realizados en un árbol binario de búsqueda desequilibrado, pero
precedido o seguido por una o más de las llamadas "rotaciones AVL".

Rotaciones
El reequilibrado se produce de abajo hacia arriba sobre los nodos en los que se produce el
desequilibrio. Pueden darse dos casos: rotación simple o rotación doble; a su vez ambos casos
pueden ser hacia la derecha o hacia la izquierda.
Rotación simple a la derecha
De un árbol de raíz (r) y de hijos izquierdo (i) y derecho (d), lo que haremos será formar un
nuevo árbol cuya raíz sea la raíz del hijo izquierdo, como hijo izquierdo colocamos el hijo
izquierdo de i (nuestro i’) y como hijo derecho construimos un nuevo árbol que tendrá como
raíz, la raíz del árbol (r), el hijo derecho de i (d’) será el hijo izquierdo y el hijo derecho será el
hijo derecho del árbol (d)

4
Rotación simple a la izquierda
De un árbol de raíz (r) y de hijos izquierdo (i) y derecho (d), consiste en formar un nuevo árbol
cuya raíz sea la raíz del hijo derecho, como hijo derecho colocamos el hijo derecho de d
(nuestro d’) y como hijo izquierdo construimos un nuevo árbol que tendrá como raíz la raíz del
árbol (r), el hijo izquierdo de d será el hijo derecho (i’) de r y el hijo izquierdo será el hijo
izquierdo del árbol (i).
Precondición: Debe tener hijo derecho no vacío.
Si la inserción se produce en el hijo derecho del hijo izquierdo del nodo desequilibrado (o
viceversa) hay que realizar una doble rotación.
Rotación doble a la derecha
La Rotación doble a la Derecha son dos rotaciones simples, primero rotación simple izquierda y
luego rotación simple derecha.

5
Rotación doble a la izquierda
La Rotación doble a la Izquierda son dos rotaciones simples, primero rotación simple derecha y
luego rotación simple izquierda.

Inserción
La inserción en un árbol de AVL puede ser realizada insertando el valor dado en el árbol como
si fuera un árbol de búsqueda binario desequilibrado y después retrocediendo hacia la raíz,
rotando sobre cualquier nodo que pueda haberse desequilibrado durante la inserción.
Proceso de inserción:
1 buscar hasta encontrar la posición de inserción o modificación (proceso idéntico a inserción
en árbol binario de búsqueda)
2 insertar el nuevo nodo con factor de equilibrio “equilibrado”
3 desandar el camino de búsqueda, verificando el equilibrio de los nodos, y reequilibrando si es
necesario
Debido a que las rotaciones son una operación que tienen complejidad constante y a que la
altura está limitada a O (log(n)), el tiempo de ejecución para la inserción es del orden O (log(n)).
Extracción
El procedimiento de borrado es el mismo que en el caso de árbol binario de búsqueda. La
diferencia se encuentra en el proceso de reequilibrado posterior. El problema de la extracción
puede resolverse en O (log(n)) pasos. Una extracción trae consigo una disminución de la altura
de la rama donde se extrajo y tendrá como efecto un cambio en el factor de equilibrio del nodo
padre de la rama en cuestión, pudiendo necesitarse una rotación.
Esta disminución de la altura y la corrección de los factores de equilibrio con sus posibles
rotaciones asociadas pueden propagarse hasta la raíz.

6
 Borrar A, y la nueva raíz será M.

 Borrado A, la nueva raíz es M. Aplicamos la


rotación a la izquierda.

 El árbol resultante ha perdido altura.

7
En el borrado pueden ser necesarias varias operaciones de restauración del equilibrio, y hay
que seguir comprobando hasta llegar a la raíz.

Métodos

Insertar
Cómo ya sabemos una fundamental característica del “Árbol binario AVL” es el equilibrio que
debe contener sus ramas, para ello la altura entre los niveles no pueden diferir de 1. Por
ejemplo:

3 3
0 2
2
1

1 0 0
1 1

0
0

Para conseguir este equilibrio debemos hacer uso de los siguientes 2 métodos:
-Rotación simple:
La “Rotación simple” se utiliza cuando el desequilibrio se haya al tratar de insertar un nuevo
nodo en el hijo con mayor valor o el más pequeño. Por ejemplo:

8
10

6 15

Deseamos insertar el un nodo con valor 22 y su


lugar correspondiente seria como hijo derecho
del 19, sin embargo, rompe el equilibro en el 14 19
árbol.

22

10
0 2

6 15
Después de insertar el nodo (de manera teórica) 1 1
el árbol evaluara las alturas, encontrando el
problema.
14 19

22

9
10

6 15

El programa procederá a hacer la “Rotación


Simple”
14 19

22

2 15
1 1

Por último, el padre queda como hijo y el hijo


como padre. Como se puede observar el árbol 10 19
queda equilibrado. 0 0 0

6 14 22

Como se ve en el ejemplo, el hijo que sufre el desequilibrio va a pasar a ser el padre mientras
que el padre pasa a ser el hijo izquierdo y conserva a su hijo izquierdo, ahora el que antes era
padre tendrá como hijo derecho al que antes era el hijo izquierdo del hijo y el hijo ahora es
padre.
1 public Nodo<T> rotacionDerecha(Nodo<T> dato) {
2 Nodo<T> aux = dato.getHijoDerecho();
3 dato.setHijoDerecho(aux.getHijoIzquierdo());
4 aux.setHijoIzquierdo(dato);
5
6 dato.setFe(Math.max(obtenerFE(dato.getHijoIzquierdo()),
7 obtenerFE(dato.getHijoDerecho())) + 1);
8 aux.setFe(Math.max(obtenerFE(aux.getHijoIzquierdo()),
9 obtenerFE(aux.getHijoDerecho())) + 1);
10
11 return aux;
12 }
1 public Nodo<T> rotacionIzquierda(Nodo<T> dato) {
2 Nodo<T> aux = dato.getHijoIzquierdo();
3 dato.setHijoIzquierdo(aux.getHijoDerecho());
4 aux.setHijoDerecho(dato);
5

10
6 dato.setFe(Math.max(obtenerFE(dato.getHijoIzquierdo()),
7 obtenerFE(dato.getHijoDerecho())) + 1);
8 aux.setFe(Math.max(obtenerFE(aux.getHijoIzquierdo()),
9 obtenerFE(aux.getHijoDerecho())) + 1);
10
11 return aux;
12 }
-Rotación Doble:
La “Rotación Doble” sucede cuando se desea insertar un nuevo nodo en un hijo intermediario
(ni que sea el más pequeño ni el más grande), entonces sucederá lo siguiente:

10

6 15

Deseamos insertar un nodo con valor 11 y su


lugar correspondiente seria como hijo izquierdo
del 14, sin embargo, rompe el equilibro en el 14 19
árbol.

11

10
0 2

6 15
Después de insertar el nodo (de manera teórica) 1 1
el árbol evaluara las alturas, encontrando el
problema.
14 19
0

11

11
10

6 15

El programa procederá a hacer la “Rotación


Doble”
14 19

11

10

11
6

Primero se cambia al abuelo por el nieto y el 15


abuelo conserva a sus hijos.

14 19

2
11
1
1

Por último, se cambia al bisabuelo por el bisnieto 10 15


y este pasa a ser el hijo izquierdo. Como se 0
puede observar el árbol queda equilibrado. 0 0

6
14 19

12
Como se puede observar se ejecuta una rotación doble en donde el “bisnieto” pasa a ser el
padre del bisabuelo y del abuelo. El “abuelo” que ahora es hijo derecho conserva a sus 2 hijos
y el “bisabuelo” que ahora es hijo izquierdo conserva a su hijo izquierdo.
Aquí presentamos los métodos de rotación doble izquierda y derecha.
1 public Nodo<T> rotacionDobleDerecha(Nodo<T> dato) {
2 Nodo<T> aux;
3 dato.setHijoDerecho(rotacionIzquierda(dato.getHijoDerecho()));
4 aux = rotacionDerecha(dato);
5
6 return aux;
7 }
1 public Nodo<T> rotacionDobleIzquierda(Nodo<T> dato) {
2 Nodo<T> aux;
3 dato.setHijoIzquierdo(rotacionDerecha(dato.getHijoIzquierdo()));
4 aux = rotacionIzquierda(dato);
5
6 return aux;
7 }
A continuación, veremos finalmente el método insertar con todas las variantes de los casos
antes vistos.
1 public Nodo<T> insertar(Nodo<T> dato, Nodo<T> subArbol) {
2 Nodo<T> nPadre = subArbol;
3 if (dato.comparaMenor(subArbol.getDato())) {
4
5 if (subArbol.getHijoIzquierdo() == null) {
6 subArbol.setHijoIzquierdo(dato);
7 } else {
8 subArbol.setHijoIzquierdo(insertar(dato,
9 subArbol.getHijoIzquierdo()));
10
11 if ((obtenerFE(subArbol.getHijoIzquierdo()) - obtenerFE(subArbol
12 .getHijoDerecho())) == 2) {
13 if (dato.comparaMenor(subArbol.getHijoIzquierdo().getDato())) {
14 nPadre = rotacionIzquierda(subArbol);
15 System.out.println("rotacion izq");
16 System.out.println("con el dato: " + dato.getDato());
17 } else {
18 nPadre = rotacionDobleIzquierda(subArbol);
19 System.out.println("rotacion d izq");
20 System.out.println("con el dato: " + dato.getDato());
21 }
22 }
23 }
24 } else if (!dato.comparaMenor(subArbol.getDato())) {
25 if (subArbol.getHijoDerecho() == null) {
26 subArbol.setHijoDerecho(dato);
27 } else {
28 subArbol.setHijoDerecho(insertar(dato,
29 subArbol.getHijoDerecho()));

13
30
31 if ((obtenerFE(subArbol.getHijoDerecho()) - obtenerFE(subArbol
32 .getHijoIzquierdo())) == 2) {
33 if (!dato.comparaMenor(subArbol.getHijoDerecho().getDato())) {
34 nPadre = rotacionDerecha(subArbol);
35
36 } else {
37 nPadre = rotacionDobleDerecha(subArbol);
38 }
39 }
40 }
41 } else {
42 System.out.println("Nodo duplicado");
43 }
44
45 if (subArbol.getHijoIzquierdo() == null
46 && subArbol.getHijoDerecho() != null) {
47 subArbol.setFe(subArbol.getHijoDerecho().getFe() + 1);
48 } else if (subArbol.getHijoDerecho() == null
49 && subArbol.getHijoIzquierdo() != null) {
50 subArbol.setFe(subArbol.getHijoIzquierdo().getFe() + 1);
51 } else {
52 subArbol.setFe(Math.max(obtenerFE(subArbol.getHijoIzquierdo()),
53 obtenerFE(subArbol.getHijoDerecho())) + 1);
54 }
55 return nPadre;
56 }
57
58 public void insertar(T dato) {
59 Nodo<T> aux = new Nodo<T>(dato);
60 if (raiz == null) {
61 raiz = aux;
62 } else {
63 raiz = insertar(aux, raiz);
64 }
65 }
Eliminar
Cuando queremos eliminar un nodo de árbol debemos tener siempre en cuenta el balanceo de
este, ya que si se produce un desequilibrio deja de ser un árbol. Para eliminar el nodo se usará
los métodos de “Rotación simple” o “Rotación doble” entonces existen ciertos casos al
momento en que se localiza el nodo a eliminar:
-Si es un nodo hoja entonces simplemente se elimina

B C

A
14
C

- Ahora que el nodo a eliminar tiene un hijo entonces lo único que se hace es eliminar el nodo y
que su hijo ocupe su lugar.

B C

D C

-Si el nodo que queremos eliminar tiene un subárbol izquierdo o derecho, o ambos, en esta
ocasión se tendrá que iniciar una búsqueda que encuentre el nodo en el subárbol derecho con
el valor más pequeño (para ello únicamente se escanean los hijos izquierdos) y cuando se
encuentre el de menor valor entonces será asignado como nuevo padre y el nodo a eliminar
obtendrá el valor del nodo menor; este procedimiento nos asegura que el nuevo valor que se
ponga en el nodo no que tenga hijos izquierdos, pero puede que tenga un hijo derecho y por
último se hacen los anteriores pasos.

15
A

B C

D E

F G

F C

D E

F G

F C

D E

H G

16
1 public Nodo<T> eliminarP(T dato) {
2 Nodo<T> tmp, nodo, previo = null, n = raiz;
3 while (n != null && n.getDato() != dato) {
4 previo = n;
5 if (n.comparaMenor(dato)) {
6 n = n.getHijoDerecho();
7 } else {
8 n = n.getHijoIzquierdo();
9 }
10 }
11 nodo = n;
12 if (n != null && n.getDato() == dato) {
13 if (nodo.getHijoDerecho() == null) {
14 nodo = nodo.getHijoIzquierdo();
15 } else if (nodo.getHijoIzquierdo() == null) {
16 nodo = nodo.getHijoDerecho();
17 } else {
18 tmp = nodo.getHijoIzquierdo();
19 while (tmp.getHijoDerecho() != null) {
20 tmp = tmp.getHijoDerecho();
21 }
22 tmp.setHijoDerecho(nodo.getHijoDerecho());
23 nodo = nodo.getHijoIzquierdo();
24 }
25 if (n == raiz) {
26 raiz = nodo;
27 } else if (previo.getHijoIzquierdo() == n) {
28 previo.setHijoIzquierdo(nodo);
29 } else {
30 previo.setHijoDerecho(nodo);
31 }
32 previo.setFe(previo.getFe() - 1);
33 } else if (raiz != null) {
34 System.out.println("No existe el dato");
35 }
36 return previo;
37 }
38
39 public void eliminar(T dato) {
40 Nodo<T> n = eliminarP(dato);
41 Nodo<T> subArbol = raiz;
42 if (subArbol.comparaMenor(dato)) {
43 subArbol = subArbol.getHijoIzquierdo();
44 int a = n.getFe() - subArbol.getFe();
45 if (a == -2) {
46 subArbol = rotacionIzquierda(subArbol);
47 }
48 }
49 }

Recorrer amplitud
Este método recorre el subárbol izquierdo y el subárbol derecho, e imprime los datos que se
encuentran declarados en los nodos en orden de padre e hijos y en forma de pila. Por ejemplo:

17
12 12
6
15 15
6
5
9
5 9 18
18

Código:
1 public void recAmplitud() {
2 Nodo<T> n = raiz;
3 Cola<Nodo<T>> cola = new Cola<>();
4 if (n != null) {
5 cola.insertar(raiz);
6 while (!cola.esVacia()) {
7 n = cola.sacar();
8 System.out.println(n.getDato());
9
10 if (n.getHijoIzq() != null) {
11 cola.insertar(n.getHijoIzq());
12 }
13 if (n.getHijoDer() != null) {
14 cola.insertar(n.getHijoDer());
15 }
16 }
17 }
18 }
Altura
La altura es el todo que nos dará la distancia del nodo requerido hasta la hoja más profunda, es
un método fácil de implementar ya que únicamente se tiene que contar los nodos por los que
se están pasando hasta el último. Por ejemplo:

B C

D E

F G

H
Altura de:
A=4

18
C=0
F=1
D=0
E=2
Código:
1 public int altura(T dato) {
2 Nodo<T> n = raiz;
3 while (!(n.getDato().equals(dato))) {
4 if (n.comparaMenor(dato)) {
5 n = n.getHijoDer();
6 } else if (!n.comparaMenor(dato)) {
7 n = n.getHijoIzq();
8 }
9 }
10 return alturaDos(n);
11 }
12
13 public int alturaDos(Nodo<T> n) {
14 int altIzq, altDer;
15 if (n == null) {
16 return -1;
17 } else {
18 altIzq = alturaDos(n.getHijoIzq());
19 altDer = alturaDos(n.getHijoDer());
20 }
21 if (altIzq > altDer) {
22 return altIzq + 1;
23 } else {
24 return altDer + 1;
25 }
26 }

Conclusión
Por medio del siguiente trabajo logramos construir y analizar la capacidad y funcionamiento de
diversos métodos que son empleados al momento de construir arboles AVL y poder emplearlos
en distintos objetivos como lo son la búsqueda de información y la organización de esta misma.
Del mismo modo al hacer uso de este algoritmo, garantizamos que el acceso a los datos será
siempre de una manera mucho más eficiente y rápida con la cual siempre mantendremos el
orden y la jerarquía de los datos que queremos usar.
De igual manera, al construir nuestro árbol AVL, podemos observar que los métodos de
rotación simple o doble, así como los de insertar y eliminar, hacen uso de la recursividad, de
esta manera, logrando reducir de manera práctica y en gran medida el tiempo y forma de
resolución de estos problemas.
También, al lograr construir nuestro árbol AVL, podemos organizar la información que se
almacena en él, llevando de esta manera un control y un acceso a ella de manera rápida y fácil,

19
permitiéndonos realizar tareas ya sea de ordenamiento, resolución de problemas matemáticos
o de búsqueda con un costo reducido y de análisis menor a lo que seria sin la ayuda de tales
métodos.
Cabe resaltar que para la tarea de resolución de problemas aritméticos y de construcción de
árboles, utilizamos estructuras ya antes vistas como lo son pilas y colas, de esta manera,
englobando y llegando a la construcción de aplicaciones con mayor capacidad de análisis y de
procesamiento. Siendo esta el objetivo final que buscamos obtener, que es el de la resolución
de problemas cada vez mas complejos de manera más sencilla.
A través de estos métodos conseguimos dar pie al análisis de tareas cada vez mas complejas y
de mayor capacidad para futuros casos.

Bibliografía
Luis Joyanes y Ignacio Zahonero. (2007). Estructura de datos en C++. Madrid: McGRAW-
HILL/INTERAMERICANA DE ESPAÑA, S. A. U. .
Martínez, L. J. (2008). Estructura de datos en Java. Basauri: MCGRAW-
HILL/INTERAMERICANA DE ESPAÑA, S. A. U. .
Yedidyah Langsman, M. J. (1997). Estructuras de datos con C y C++. México: PRENTICE-
HALL HISPANOAMERICANA, S.A.
Allen, M. (2013). Estructura de datos en Java. Madrid, España: PEARSON

Referencias
Luis Joyanes y Ignacio Zahonero. (2007). Estructura de datos en C++. Madrid: McGRAW-
HILL/INTERAMERICANA DE ESPAÑA, S. A. U. .
Martínez, L. J. (2008). Estructura de datos en Java. Basauri: MCGRAW-
HILL/INTERAMERICANA DE ESPAÑA, S. A. U. .
Yedidyah Langsman, M. J. (1997). Estructuras de datos con C y C++. México: PRENTICE-
HALL HISPANOAMERICANA, S.A.
Juan Angel Ortiz Contreras, J. U. O. C. (2015, 6 junio). Algoritmos de ordenamiento secuencial.
Recuperado 16 octubre, 2019, de https://texttter73.wixsite.com/bloginformativoppyd/single-
post/2015/02/24/PRACTICA-1-Algoritmos-de-ordenamiento-secuencial

20

También podría gustarte