Complejidad de Inserción en Árboles AVL
Complejidad de Inserción en Árboles AVL
0 * 0 * 0 * 0 *
Estructuras de datos - Dr. Sergio A. Gómez 3 Estructuras de datos - Dr. Sergio A. Gómez 4
Comentarios Rotaciones
• Siguiendo a Goodrich & Tamassia diremos que los nodos nulos
tienen altura 0.
• Como un AVL es un árbol binario de búsqueda, la operación de
Son cuatro correspondientes a las cuatro
búsqueda no sufre alteraciones. combinaciones para la inserción de una clave a
• La únicas operaciones a modificar son la de inserción y eliminación, partir de un nodo raíz del subárbol considerado:
las cuales deben verificar que se cumpla la propiedad de balance al
finalizar la operación. 1) izquierda – izquierda: rotación simple de
• Luego de cada modificación en un nodo, rebalancearán los hijos de izquierda a derecha
dicho nodo en O(1) por medio de las llamadas “rotaciones”. 2) izquierda – derecha: Rotación doble de izquierda
• Las modificaciones se hacen desde la hoja donde se insertó el nodo
hacia la raíz siguiendo el camino de llamadas recursivas. a derecha
• Las eliminaciones las haremos perezosas (lazy): marcaremos los 3) derecha – derecha: Rotación simple de derecha
datos eliminados con un booleano. a izquierda
4) derecha – izquierda: Rotación doble de derecha
a izquierda
Estructuras de datos - Dr. Sergio A. Gómez 5 Estructuras de datos - Dr. Sergio A. Gómez 6
3 10 3
3 6 3 6 7
t2 t3
6,5
Antes de rotar Después de rotar t1 t2 t3 t4
3 10 3 8 10
t2 t3
9
Después de rotar t1 t2 t3 t4
Antes de rotar
Corresponde a una inserción en el hijo derecho de la raíz y luego en el
La inserción del 9 destrute la propiedad de AVL en el nodo 8, entonces hay que rotar.
hijo izquierdo del hijo derechos de la raíz del subárbol considerado
Estructuras de datos - Dr. Sergio A. Gómez 13 Estructuras de datos - Dr. Sergio A. Gómez 14
Implementación Java de la Inserción public class AVL<E> El árbol AVL está parametrizado con el tipo E de los rótulos
La única diferencia con el NodoABB es que en cada nodo mantengo {
la altura de dicho nodo. NodoAVL<E> raiz; Su raíz es un nodo AVL
// Archivo: [Link] Comparator<E> comp; Necesito el comparador de claves
public class NodoAVL<E>
public AVL(Comparator<E> comp) El cliente me pasa el comparador
{
{
private NodoAVL<E> padre; raiz = new NodoAVL<E>(null); Creo un nodo dummy para la raíz
private E rotulo; [Link] = comp; Salvo el comparador
private int altura; private boolean eliminado; // }
diferencia!
public void insert(E x) Insertar recorre el árbol recursivamente
private NodoAVL<E> izq, der;
{ desde la raíz con un método auxiliar
insertaux( raiz, x ); llamado insertaux
public NodoAVL<E>(E rotulo){ }
altura = 0;
// Al crear un nodo dummy anoto que su altura es 0. private int max(int i, int j ) Este método sirve para calcular el
{ máximo entre i y j
... // Resto Idem NodoABB}
return i>j ? i : j;
// setters y getters incluyendo la altura y eliminado }
Estructuras de datos - Dr. Sergio A. Gómez 15 Estructuras de datos - Dr. Sergio A. Gómez 16
}
private void insertaux( NodoAVL<E> p, E item ) { /* p es el nodo actual, la primera vez es la raíz */
if( [Link]() == null ) { // Llegué a un dummy
[Link]( item ); [Link]( 1 ); // Le seteo la altura al dummy como 1 y le hago 2 hijos
[Link]( new NodoAVL<E>( null ) ); [Link]( new NodoAVL<E>( null ) ); Complejidad temporal de la inserción
[Link]().setPadre( p ); [Link]().setPadre( p );
} else {
int comparacion = [Link]( item, [Link]() );
if( comparacion == 0 ) { eliminado=false; /* ítem ya está en el árbol => no cambia nada */ }
• Noten que las rotaciones se hacen en los nodos del
else if( comparacion < 0 ) { camino desde la raíz hasta la hoja donde se insertó la
insertaraux( [Link](), item ); // Inserto recursivamente en el HI y a la vuelta rebalanceo nueva clave.
if( [Link]( [Link]().getAltura() - [Link]().getAltura() ) > 1 ) {
// Rebalancear mediante rotaciones: testeo por rotaciones (i) o (ii) • Como las rotaciones se implementan con asignaciones
// Si estoy aca => item < x, debo testear si (item < y) o (item > y) de referencias (posiciones), cada rotación se hace en
// si item < y => rotacion (i); si item > y => rotacion (ii)
E x = [Link](); // no se usa, es solo para la explicación
tiempo constante.
E y = [Link]().getRotulo(); • La cantidad de rotaciones es del orden de la altura del
E z = [Link]().getLeft().getRotulo(); // no se usa, es solo para la explicación
int comp_item_y = [Link]( item, y );
árbol.
if( comp_item_y < 0 ) rotacion_i(p); // item < y => rotacion (i) • La altura es proporcional al logaritmo de la cantidad de
else rotacion_ii(p); // item > y => rotacion (ii)
}
nodos del árbol.
} else { • Por lo tanto, el tiempo de insertar es del orden del
/* Caso simétrico pero insertando hacia la derecha y luego testeo por rotaciones (iii) y (iv) */
}
logaritmo de la cantidad de nodos del árbol.
[Link]( max([Link]().getAltura(), [Link]().getAltura()) + 1 ); // Setear altura del nodo p
}
Estructuras de datos - Dr. Sergio A. Gómez 17 Estructuras de datos - Dr. Sergio A. Gómez 18
}
Estructuras de datos - Dr. Sergio A. Gómez 23 Estructuras de datos - Dr. Sergio A. Gómez 24
Recuperar( R, clave ) : boolean Inserción en un árbol 2-3:
SI clave está en R ENTONCES
RETORNAR true • Igual que en el ABB, siempre se inserta en una hoja siguiendo el
SINO SI R es una hoja ENTONCES camino de la búsqueda desde la raíz del árbol hasta la hoja
RETORNAR false Búsqueda en un árbol 2-3 correspondiente (esa hoja ya va a tener por lo menos 1 clave).
SINO
SI R tiene una clave k ENTONCES
• Si la hoja ahora tiene 2 claves, terminamos.
SI clave < k ENTONCES RETORNAR Recuperar( Ti(R), clave ) • Si la hoja ahora tiene 3 claves, se produce un rebalse (overflow) y se
SINO RETORNAR Recuperar( Td(R), clave ) debe partir el nodo en 2 nodos, la clave del medio sube al padre,
SINO quien queda a cargo de administrar ese hijo que se duplicó y ver
SI R tiene dos claves k1 y K2 ENTONCES
SI clave < k1 ENTONCES dónde ubicar esa clave.
RETORNAR Recuperar( Ti(R), clave ) • Si el padre tenía 1 clave y 2 hijos, no hay problema, porque ahora
SINO SI clave < k2 ENTONCES tendrá 2 claves y 3 hijos.
RETORNAR Recuperar( Tm(R), clave )
SINO • Si el padre ya tenía 2 claves y 3 hijos, ahora pasaría a tener 3 claves y
RETORNAR Recuperar( Td(R), clave ) 4 hijos, lo cual no puede ocurrir. Entonces el proceso anterior se
repite.
• El proceso termina cuando terminamos en un nodo intermedio (con
2 claves o 3 hijos) o llegamos a crear una nueva raíz y el árbol crece 1
nivel.
Estructuras de datos - Dr. Sergio A. Gómez 25
• Como esteEstructuras
procesode datos - Dr. Sergio A. Gómez
tiene T(h) = O(h), ocurre que T(n) = O(log(n)). 26
1) Insertamos 10 en el árbol vacío: 4) Cualquier clave que inserte, siempre va a una hoja siguiendo el criterio de búsqueda.
Inserto 20 y 5. Como no hay rebalse porque había lugar en las hojas, terminamos.
10
12 12
2) Insertamos 15: como hay lugar en la única hoja, allí ponemos la nueva clave y
terminamos 10 15 5 10 15 20
10 15
1 3 5 40 45 1 3 5 8 40 45
1 3 5 9 40 45
Las inserciones se hacen siempre en las hojas siguiendo el orden de las claves. Estado al insertar el 2: 8
Para insertar una clave, habrá que ver si es menor o mayor a la clave de la raíz.
Insertar 10, 4, 30, 7, 6, 90 produce el árbol:
1 2 3 4 5 6 7 9 10 30 40 45 90
8
Rebalsó el 7, vamos a partir nodo reabalsado en dos y mandar la clave del medio
(es este caso 4) al padre:
4 8
1 3 4 5 6 7 9 10 30 40 45 90
1 2 3 5 6 7 9 10 30 40 45 90
Estructuras de datos - Dr. Sergio A. Gómez 39 Estructuras de datos - Dr. Sergio A. Gómez 40
Estado antes de
insertar el 100:
4 8 Árboles B: Búsqueda de k
1 2 3 5 6 7 9 10 30 40 45 90
Al insertar el 100, como 100 > 8, va hacia la hoja de más a la derecha, que
rebalsa. Entonces la partimos y subimos la clave del medio (en este caso el 40):