Árboles desplegados(splay trees)

advertisement
1
Capítulo 13
Árboles Desplegados. Splay Trees.
13.1 Definición.
Es un árbol de búsqueda autoorganizado que emplea rotaciones para mover cualquier clave
accesada, ya sea en búsqueda, inserción o descarte, a la raíz.
Esto deja a los nodos más recientemente accesados cerca de la raíz, haciendo que la posterior
búsqueda de ellos sea eficiente.
No se requiere almacenar información adicional en el nodo, ya sea el factor de balance (AVL), o
el color en árboles coloreados, o un puntero adicional en árboles 2-3.
La forma del árbol va variando de acuerdo a los nodos que son más recientemente accesados.
Fueron desarrollados por Sleator y Tarjan in 1985, en la publicación del ACM Journal, “Selforganizing Binary Search Trees” como una alternativa a los algoritmos que mantienen
balanceado un árbol binario de búsqueda.
La heurística es similar a la empleada en listas autoorganizadas, en las cuales los elementos
buscados se van colocando más cerca del inicio de la lista. Una opción conservadora, es
adelantar en una posición, el elemento buscado, cada vez que hay un acceso a esa clave; otra,
más enérgica, es llevar el elemento al inicio de la lista. Puede comprobarse que mover al frente
tiene un mejor comportamiento, en caso de distribuciones de búsqueda que cambian.
Si algo ha sido accesado, es muy probable que sea nuevamente accesado.
En el caso de los árboles splay se lleva el elemento buscado o insertado a la posición de la raíz.
En la búsqueda o la inserción bottom-up, se realiza un recorrido desde la raíz hasta encontrar el
elemento buscado; o bien hasta encontrar una hoja, en caso de inserción. Luego de lo anterior se
realiza una operación splay para mover el elemento a la posición de la raíz.
13.2 Operación splay.
La operación splay, consiste de una secuencia de dobles rotaciones, hasta que el nodo quede a
un nivel debajo de la raíz; en este caso basta una rotación simple para completar la operación.
En cada operación splay se hace ascender al nodo en uno o dos niveles, dependiendo de su
orientación relativa respecto de su nodo abuelo.
Profesor Leopoldo Silva Bijit
20-01-2010
2
Estructuras de Datos y Algoritmos
Hay tres casos:
Zig: el nodo es un hijo izquierdo o derecho (Zag) de la raíz. Sin abuelo.
Zig-Zag: El nodo es un hijo izquierdo de un hijo derecho; o un hijo derecho de un hijo izquierdo
(Zag-Zig).
Zig-zig: El nodo es un hijo izquierdo de un hijo izquierdo; o un hijo derecho de un hijo derecho
(Zag-Zag).
Gráficamente:
y
x
Zig.
x
C
y
A
A
B
B
C
Figura 13.1. Operación Zig.
Si t apunta al padre de x, la rotación simple, en este caso, se logra con: t=rrot(t);
Se rota el padre de x, a la derecha.
Pasar de la figura de la derecha hacia la de la izquierda se denomina Zag, y la operación que la
logra es: t=lrot(t).
Zig-Zig
x
z
y
y
A
D
z
x
B
C
A
C
B
D
Figura 13.2. Operación Zig-Zig.
Si inicialmente t apunta al abuelo de x. Se rota el abuelo de x, y luego el padre del nodo x.
Se logra con la secuencia :
t=rrot(t);
t=rrot(t);
Pasar de la figura derecha a la izquierda, el ascenso de z a la raíz se denomina Zag-zag.
Zig-Zag.
z
x
y
D
z
y
x
A
A
B
B C
C
Figura 13.3. Operación Zig-Zag.
Profesor Leopoldo Silva Bijit
20-01-2010
D
Árboles desplegados. Splay trees.
3
Se rota el padre de x a la izquierda, y luego se rota el nuevo padre de x a la derecha. La imagen
especular se denomina Zag-Zig.
Mover a la raíz.
Debe notarse que mover un nodo hacia la raíz, siguiendo en forma inversa la trayectoria de
búsqueda desde a raíz hasta el nodo, no es enteramente equivalente a las dobles rotaciones
propuestas en árboles splay. Las operaciones Zig, Zag, Zig-Zag y Zag-Zig, son equivalentes a
las que produce el mover hacia la raíz; la diferencia está en las operaciones Zig-Zig y Zag-Zag.
En el caso de mover hacia la raíz, se rota el padre de x a la derecha y luego el nuevo padre de x,
a la derecha.
x
z
y
z
A
D
x
y
D
C
A
B
B
C
Figura 13.4. Mover x hacia la raíz.
Medir el efecto de estos dos tipos de rotaciones requiere un análisis de costos denominado
“Análisis amortizado”. Se puede verificar que el costo amortizado de m operaciones splay sobre
un árbol con n nodos, es: O( (m + n) * log2(n + m) )
Es con este fundamento que se eligen las dobles rotaciones en este tipo de árboles, y como se
verá a través de ejemplos, tienden a acortar la altura del árbol.
13.3 Tipos de algoritmos.
Existen dos tipos de algoritmos, bottom-up (de abajo hacia arriba) o top-down (de arriba hacia
abajo).
13.3.1. Splay Bottom-up.
Las operaciones de búsqueda, inserción y descarte de un nodo se efectúan en forma similar a un
árbol binario de búsqueda. Luego se realiza una operación splay sobre un nodo.
En búsqueda el nodo es el que contiene el valor buscado, o el padre de la hoja si no lo
encuentra. En inserción, el nodo sobre el que se aplica la operación splay es el de igual valor al
buscado, si ya existía; o el nuevo nodo si éste no estaba en el árbol.
En bottom-up se requiere descender de la raíz hasta el nodo al que se le aplicará la operación
splay. Luego se van efectuado las rotaciones a medida que se asciende. Es decir se recorre el
árbol dos veces.
A partir del nodo, al que se le aplicará la operación, se asciende hasta encontrar el abuelo, y se
efectúa la rotación doble que corresponda; si no existe abuelo, pero sí padre, se efectúa
rotación simple.
Profesor Leopoldo Silva Bijit
20-01-2010
4
Estructuras de Datos y Algoritmos
13.3.2. Ejemplos de operaciones splay bottom-up.
Splay(3, root);
1
1
1
2
3
2
6
6
Zig-Zig
2
3
2
4
Zag
4
4
4
6
6
Zag-Zig
3
5
1
3
5
5
5
Figura 13.5. Operación Splay(3, root)
Splay(1, root);
7
6
6
5
2
1
Zig-Zig
4
2
Zig-Zig
2
1
6
4
1
Zag-Zag
4
1
6
5
4
3
7
7
2
5
7
5
3
3
3
Figura 13.6. Operación Splay(1, root)
Las operaciones tienden a disminuir la altura.
La figura siguiente, muestra la operación mover el nodo con valor 1, a la raíz. Lo que permite
comparar las formas de los árboles generados mediante las dos operaciones. Ver Figuras 13.6 y
13.7.
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
5
MuevealaRaiz(1, root)
7
1
6
7
5
6
Mueve a la raíz
4
5
3
4
2
3
1
2
Figura 13.7. Operación Mover a la raíz.
Insertar nodo con valor 5.
5
7
7
1
6
3
8
4
1
4
2
3
8
3
2
Zig-Zag
7
8
1
6
5
2
4
6
Insert(5, root)
5
7
3
4
1
Zag-Zig
5
6
8
2
Splay(5, root)
Figura 13.8. Operación Insertar(5, root)
Para el diseño de descarte existen varias posibilidades.
a) Proceder como en árbol binario de búsqueda, y no emplear operaciones splay, considerando
que si algo se borra, no significa que se intentará buscar en la proximidad del elemento borrado.
b) Si lo busca y no lo encuentra efectúa una operación splay con el padre del buscado. Si lo
encuentra, efectúa operación splay sobre el nodo, dejándolo en la raíz. Luego efectúa una
operación splay con el nodo con mayor clave en el subárbol izquierdo; a continuación se
descarta la raíz; y finalmente se enlaza el subárbol derecho con el subárbol izquierdo.
Profesor Leopoldo Silva Bijit
20-01-2010
6
Estructuras de Datos y Algoritmos
La siguiente figura ilustra la alternativa b).
La operación descarte(4, root), ubica el nodo con valor 4, y lo lleva a la raíz. Luego se efectúa:
splay(3, TL), se descarta el nodo con valor 4, y se efectúa la unión de dos subárboles (join).
Descartar(4, root).
7
3
4
4
6
6
TL
6
2
TL
2
6
3
5
1
5
3
2
4
1
7
5
1
7
2
5
7
1
3
Figura 13.9. Operación Descartar(4, root)
Descartar(6, root)
3
6
TL
1
4
Zag-zag
2
3
6
5
7
Zag
7
5
8
5
5
7
4
1
8
Splay(6, root)
6
4
8
1
8
3
1
3
2
7
LiberarRaíz
Splay(5, TL)
2
join(L,R)
2
Figura 13.10. Operación Descartar(6, root)
Para tener un conjunto de operaciones que consideren las propiedades de esta estructura, se
pueden definir:
accesar(i, t): Si i está en el árbol t, retorna un puntero al nodo encontrado, en caso contrario
retorna NULL. Busca el nodo con valor i, y efectúa splay en ese nodo; si no lo encuentra,
efectúa la operación splay con el último nodo accesado buscando i.
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
7
join (L, R): Retorna árbol formado por la combinación del árbol L y el árbol R, asumiendo que
todos los ítems de L tienen claves menores que cualquier item de R.
Para esto aplica splay en el nodo con mayor valor de L, luego agrega R como subárbol derecho
de la raíz.
split (i, t): Parte el árbol t en dos subárboles, L que contiene todos los items con claves menores
a i; y en R deja los nodos con claves mayores que i.
Realiza accesar(i, t) luego parte el árbol en la raíz.
inserte(i, t):
Realiza split(i, t), luego convierte i en la raíz de los dos árboles que retorna split.
descartar(i, t):
Realiza accesar(i, t) luego descarta la raíz y realiza la unión de los subárboles.
13.3.2. Splay top-down.
Se parte el árbol en dos subárboles, uno con claves menores al buscado y otro con claves
mayores al buscado, y a medida que se desciende se van efectuado las rotaciones. Cuando se
encuentra el nodo en la raíz del subárbol central, se unen los subárboles, dejando como raíz al
nodo.
Cada vez que se desciende desde un nodo x, por un enlace izquierdo, entonces x y su subárbol
derecho serán mayores que el nodo (que será insertado o que es buscado). De esta forma se
puede formar un subárbol, con x y su subárbol derecho, sea este subárbol R. El caso simétrico,
que se produce cuando se sigue un enlace derecho, permite identificar el subárbol izquierdo de
la nueva raíz, sea este subárbol denominado L.
Como se recorre sólo una vez, ocupa la mitad del tiempo que el bottom-up.
Se mantienen punteros a L y R, y punteros a los puntos de inserción de nuevos nodos en L y R;
éstos son el hijo derecho del máximo elemento de L; y el hijo izquierdo del mínimo elemento de
R. Estas variables evitan la necesidad de recorrer L y R; los nodos y subárboles que se
agreguen a L o R, no cambian sus posiciones en L o R.
A partir de la raíz se desciende hasta encontrar un posible nieto, se efectúa la operación
pasando el abuelo y el padre a los subárboles L y R; el nieto queda en la raíz del árbol central.
Si se encuentra el nodo se efectúa un join final.
Profesor Leopoldo Silva Bijit
20-01-2010
8
Estructuras de Datos y Algoritmos
Zig.
L
R
X
L
Y
R
L
Y
X
X
YL
XR
YR
YL
R
Y
YL
YR
YR
XR
XR
Figura 13.11. Top-down Zig
Se aplica operación splay al nodo con valor Y. Mediante rotación derecha Y llega a ser la raíz,
entonces el nodo X y su subárbol derecho (XR), se convierten en el hijo izquierdo del nodo con
menor valor en R. En este caso, Y pasa a ser la raíz del subárbol central.
Si t apunta a X, y si se tienen los punteros a punteros l y r, definidos según:
arbol *l=&L, *r=&R;
Se comienza a descender efectuando: p = t->left; entonces p apunta a Y. Si p no es nulo, y si el
valor sobre el que se realiza splay no es mayor ni es menor que la clave Y (ésta es la condición
para efectuar un Zig), entonces la siguiente secuencia, transforma el diagrama de la izquierda en
el de la derecha:
*r=t ; pega nodo X al subárbol R
r=&(t->left); mantiene puntero al menor descendiente de R.
t=t->left ; deja t apuntando a la nueva raíz (Y en el caso del ejemplo).
El siguiente macro realiza la operación Zig top-down:
#define rlink(t) (*r=(t), r=&((t)->left), (t)=(t)->left)
Zig-Zig
L
R
X
L
R
Z
Y
Y
ZL
XR
ZR
Z
X
YR
ZL
ZR
YR
Figura 13.12. Top-down Zig-Zig
Profesor Leopoldo Silva Bijit
20-01-2010
XR
Árboles desplegados. Splay trees.
9
Descendiendo buscando un nodo; cuando se llega a Z, se aplica Zig-Zig. Luego se extrae ZR,
que después de la operación Zig-Zig, es el hijo izquierdo del nodo Y, y se coloca como subárbol
derecho de Z; luego Y se liga como hijo izquierdo del menor valor en R.
Si t apunta a X, y p = t->lext, la condición p diferente de nulo y (p->valor) mayor que el valor
sobre el que se realiza la operación splay, se tiene la condición para la operación Zig-Zig. Se
logra la transformación, con la secuencia: t=trot(t); rlink(t);
Zig-Zag.
L
R
X
L
Y
Y
X
XR
ZL
Z
YL
R
Z
ZL
ZR
YL
XR
ZR
Figura 13.13. Top-down Zig-Zag
Descendiendo buscando un nodo; cuando se llega a Z, se aplica Zig-Zag. Luego se pega Y a L,
y X a R. Quedando Z en la raíz del árbol central.
Si t apunta a X, y p = t->lext, la condición p diferente de nulo y (p->valor) menor que el valor
sobre el que se realiza la operación splay, se tiene la condición para la operación Zig-Zag. Se
logra la transformación, con la secuencia: rlink(t); llink(t);
Con: #define llink(t) (*l=(t), l=&((t)->right), (t)=(t)->right)
Después de rlink(t), t apunta al nodo Y.
La descripción de llink es:
*l=t ; pega Y y su subárbol izquierdo a L
l=&(t->right) ; mantiene puntero al mayor descendiente de L.
t=t->right; deja t apuntando a la nueva raíz (Z en el caso del ejemplo).
Join.
L
X
R
X
L
XL
R
XR
XL
Figura 13.14. Top-down Join.
Profesor Leopoldo Silva Bijit
20-01-2010
XR
10
Estructuras de Datos y Algoritmos
Cuando el nodo X, sobre el que originalmente se deseaba efectuar la operación splay, llega a
estar en la raíz del subárbol central, se rearma el árbol, mediante la operación join. XL será el
hijo derecho del máximo elemento de L; y XR será el hijo izquierdo del mínimo valor de R.
Debe observarse que X es menor que los nodos en XR, y que éstos son menores que los que ya
pertenecen a R. También X es mayor que los nodos en XL, y éstos son mayores que los que
pertenecen a L.
Si L y R eran los punteros a las raíces de los subárboles izquierdo y derecho respectivamente, la
secuencia siguiente implementa la transformación de la Figura 13.14:
*l = t->left; *r = t->right;
t->left=L; t->right=R;
Ejemplo top-down.
Asumiendo que se busca E.
Se encuentra C, descendiendo dos nodos; se pasan A y B a R. (Zig-Zig).
L
R
A
L
R
C
D
B
B
A
E
C
D
E
Figura 13.15. Top-down Zig-Zig en C.
Descendiendo dos niveles, se encuentra E, se deja en la raíz, con Zig-Zag. C se pega a L; D al
nuevo R.
L
C
R
D
C
B
E
E
L
R
B
A
A
D
Figura 13.16. Top-down Zig-Zag en E.
Finalmente se efectúa el join.
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
11
E
B
C
D
A
Figura 13.17. Top-down Join.
13.4. Animaciones.
http://www.ibr.cs.tu-bs.de/lehre/ss98/audii/applets/BST/SplayTree-Example.html
http://www.cs.technion.ac.il/~itai/ds2/framesplay/splay.html
http://webpages.ull.es/users/jriera/Docencia/AVL/AVL%20tree%20applet.htm
13.5. Códigos.
/* splay.h*/
/*
* 1985 D. D. Sleator R. E. Tarjan
*/
typedef int tipoclave;
typedef struct moldenode {
tipoclave clave; /* Clave */
struct moldenode *left, *right;
} nodo, *arbol;
/* Definiciones de macros */
#define max(A,B) ((A)>(B)?(A):(B))
#define
search(valor,t)
((t)=splayBU((valor),(t),0),
>clave==(valor))?(t):NULL)
//funciones definidas en splay.c Pueden invocarse si se incluye splay.h
extern arbol splayBU(tipoclave, arbol, int);
extern arbol splayTD(tipoclave, arbol);
extern arbol insertar(tipoclave, arbol);
extern arbol borrar(tipoclave, arbol);
extern int AlturaArbol(arbol);
extern int ContarNodos(arbol);
extern arbol BorraArbol(arbol);
/* end of splay.h */
/* splay.c */
/*
* Árbol binario autoorganizado.
*/
#include <stdlib.h>
#include <stdio.h>
Profesor Leopoldo Silva Bijit
20-01-2010
((t)!=NULL&&(t)-
12
Estructuras de Datos y Algoritmos
#include "splay.h"
//prototipos de funciones locales
arbol sucesor(arbol t) ;
static arbol join(arbol, arbol);
arbol descartar(tipoclave valor, arbol t);
static arbol lrot(arbol);
static arbol rrot(arbol);
static arbol CreaNodo(tipoclave);
static void LiberaNodo(arbol);
static void Error(int,tipoclave);
void ImprimeNodo(arbol t, int h); //test
void MuestraArbol(arbol t, int h);
arbol insertarrecursivo(tipoclave valor, arbol T);
arbol CreaArbol(arbol t, tipoclave a[]);
//Variables Globales y Definiciones.
static arbol NodoInsercion=NULL; /* Variable temporal, usada en insert, y por lo tanto en splay*/
static int flag; /* variable de estado */
/*
* Bottom up
*/
#define Root 0
#define Zag 1
#define Zig 2
#define NotFind 0
#define Find 1
#define Zig_Zig 2
#define Zig_Zag 3
#define Zag_Zag 4
#define Zag_Zig 5
arbol splay(tipoclave valor, arbol t, int fw)
{
if (t == NULL && NodoInsercion == NULL) {
flag=NotFind; /* árbol vacio o no lo encontró en búsqueda*/
return NULL;
}
else if (t == NULL && NodoInsercion != NULL) { /* encuentra posición para insertar */
t=NodoInsercion; /* Lo inserta */
NodoInsercion=NULL; /* Limpia variable global */
flag=Find; //comienza el ascenso y la operación splay.
return t;
}
else if (t->clave == valor) { /* Lo encuentra antes de llegar a una hoja */
flag=Find; //comienza operación splay. No marca global NodoInsercion (3).
return t; //retorna puntero al encontrado
}
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
13
else if (t->clave < valor) {
t->right=splay(valor,t->right, Zag); //desciende por la derecha
if (flag) { /* rotaciones sólo si estaba en el árbol */
if (flag==Zag_Zag) {
t=lrot(t);
t=lrot(t); //efectúa doble rotación LL
flag=Find; //resetea al ascender dos niveles.
}
else if (flag==Zag_Zig) {
t=lrot(t); //rota el abuelo a la izquierda. (2)
flag=Find; //resetea después de la doble rotación.
}
else if (fw==Zag)
flag=Zag_Zag; //se juntan dos seguidas ascendiendo por la derecha
else if (fw==Zig) { //está procesando Zig, y la anterior era Zag.
t=lrot(t); //rota el padre a la izquierda (1)
flag=Zig_Zag;
}
else /* (fw==Root) */
t=lrot(t); //efectúa Zag, un nivel bajo la raíz
}
}
else { /* (t->clave < valor) */
t->left=splay(valor,t->left,Zig); //desciende por la izquierda
if (flag) { /* rotaciones sólo si estaba en el árbol */
if (flag==Zig_Zig){
t=rrot(t);
t=rrot(t); //efectúa doble rotación RR
flag=Find; //resetea al ascender dos niveles.
}
else if (flag==Zig_Zag){
t=rrot(t); //rota el abuelo a la derecha (1)
flag=Find; //resetea al ascender dos niveles.
}
else if (fw==Zig)
flag=Zig_Zig; //se juntan dos seguidas ascendiendo por la izquierda
else if (fw==Zag) { //está procesando Zag, y la anterior era Zig.
t=rrot(t); //rota el padre a la derecha (2)
flag=Zag_Zig;
}
else /* (fw==Root) */
t=rrot(t); //efectúa Zig, un nivel bajo la raíz
}
}
return t;
}
Profesor Leopoldo Silva Bijit
20-01-2010
14
Estructuras de Datos y Algoritmos
/*
* Top Down
*/
#define rlink(t) (*r=(t), r=&((t)->left), (t)=(t)->left)
#define llink(t) (*l=(t), l=&((t)->right), (t)=(t)->right)
arbol splayTD(tipoclave valor, arbol t)
{
arbol L=NULL, R=NULL; /* Subárboles */
arbol *l=&L, *r=&R; /* punteros para insertar en L y R*/
arbol p;
while (t != NULL && t->clave != valor) {
if( valor < t->clave) {
p = t->left; /*Desciende por la izquierda*/
if (p != NULL && valor < p->clave) { /* Zig_Zig */
printf("Zig-Zig en %d\n",t->clave);
t=rrot(t);
rlink(t);
}
else if (p != NULL && valor > p->clave) { /* Zig_Zag */
printf("Zig-Zag en %d\n",t->clave);
rlink(t);
llink(t);
}
else if(p != NULL && valor == p->clave) /* Zig */
{
printf("Lo encontró. Zig en %d\n",t->clave);
rlink(t);
}
else if(p==NULL && NodoInsercion !=NULL)
{
printf("Zig para insertar en %d\n",t->clave);
rlink(t); //no está y debe insertarlo.
}
else if ((p==NULL) && NodoInsercion==NULL)
{
printf("Sube %d. Splay con el padre del no encontrado\n",t>clave);
break; //no está el buscado. sube el padre del no encontrado a la raíz
}
}
else { /* (valor > t->clave) */
p = t->right; /*Desciende por la derecha*/
if (p != NULL && valor > p->clave) { /* Zag_Zag */
printf("Zag-Zag en %d\n",t->clave);
t=lrot(t);
llink(t);
}
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
15
else if (p != NULL && valor < p->clave) { /* Zag_Zig */
printf("Zag-Zig en %d\n",t->clave);
llink(t);
rlink(t);
}
else if(p!=NULL && valor == p->clave)/* Zag */
{
printf("Lo encontró. Zag en %d\n",t->clave);
llink(t);
}
else if(p==NULL && NodoInsercion !=NULL)
{
printf("Zag para insertar en %d\n",t->clave);
llink(t); //no está y debe insertarlo.
}
else if ((p==NULL) && NodoInsercion==NULL)
{
printf("Sube %d .Splay con el padre del no encontrado\n",t->clave);
break; //no está el buscado. sube el padre del no encontrado a la raíz
}
}
}
if (t==NULL && NodoInsercion == NULL) { /* si busca y árbol vacío */
return t;
}
if (t == NULL && NodoInsercion != NULL) { /* */
t=NodoInsercion; /* inserta y lo deja en la raíz */
NodoInsercion=NULL; /* reinicia global */
}
if(L!=NULL) {*l = t->left; t->left =L; } /*join final*/
if(R!=NULL) {*r = t->right; t->right=R;}
return t;
}
/*
* insertar(valor, t): inserta nodo con clave igual a valor en arbol t
*/
arbol insertar(tipoclave valor, arbol t)
{
arbol p;
NodoInsercion = CreaNodo(valor); /* Crea el nodo y pega en la global */
// p=splayBU(valor, t, Root); /* Si no lo encuentra, lo inserta y lo coloca en la raíz */
p=splayTD(valor, t); /* Si no lo encuentra, lo inserta y lo coloca en la raíz */
if (NodoInsercion != NULL) { /* Si ya estaba, libera el nodo */
Profesor Leopoldo Silva Bijit
20-01-2010
16
Estructuras de Datos y Algoritmos
free(NodoInsercion);
NodoInsercion=NULL;
Error(1,valor); // Avisa error de inserción.
}
return p;
}
arbol buscar(tipoclave valor, arbol t)
{
arbol p;
NodoInsercion = NULL; /* */
//p=splayBU(valor, t, Root); /* si lo encuentra, lo coloca en la raíz. */
p=splayTD(valor, t); /* si lo encuentra, lo coloca en la raíz. */
if(p==NULL) Error(2,valor); // Busca en árbol vacío.
return p;
}
arbol sucesor(arbol t) /* Algoritmo iterativo */
/*menor descendiente de subárbol derecho */
{ arbol p;
if(t!=NULL) p = t->right; else return(NULL);
if(p==NULL) return(NULL);
if (p->left == NULL) /* No hay hijo izq. */
return (p); /* Retorna el menor */
while ( p->left != NULL) { /* Mientras no tenga hijo izq descender por la izq */
t = p;
p = p->left;
}
/*Al terminar el while p apunta al menor descendiente */
return (p);
/* Retorna el menor */
}
arbol borrar(tipoclave valor, arbol t)
{
arbol p,q,r;
NodoInsercion = NULL; /* */
p=buscar(valor, t); /* si lo encuentra, lo coloca en la raíz. */
//MuestraArbol(p, 1);
if (p==NULL) return(NULL);
r=sucesor(p);
if(r!=NULL)
{q=splayTD(r->clave, p->right);
t=join(p->left,q);
}
else t=p->left;
LiberaNodo(p);
return(t);
}
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
17
13.6. Operaciones utilitarias.
Se agrega descartar para completar las operaciones básicas.
/*
* descartar(valor, t): borra nodo con clave == valor en árbol t
* No se implementa mediante splay. Lo que se borra no se volverá a emplear.
*/
arbol descartar(tipoclave valor, arbol t)
{
arbol *p = &t;
arbol temp;
while (*p != NULL && (*p)->clave != valor) {//descenso iterativo
if((*p)->clave < valor)
p = &((*p)->right);
else
p = &((*p)->left);
}
if (*p != NULL) { /* (*p)->clave == valor. Encontró el nodo para descartar */
temp = *p;
/* Uno o dos hijos? */
if ((*p)->left == NULL)
*p = (*p)->right;
else if ((*p)->right == NULL)*p = (*p)->left;
else /* si tiene dos hijos */
*p =join((*p)->left,(*p)->right);
LiberaNodo(temp);
}
else /* No lo encontró */
Error(0,valor);
return(t);
}
//join (l, r): Retorna el árbol formado por la combinación del árbol "l", y del árbol "r".
//Se asume que cualquier item en "l" tiene valores menores que cualquier item en "r".
static arbol join(arbol l, arbol r)
{
arbol t;
arbol *p = &t;
while (l != NULL && r != NULL) {
*p = l;
l = l->right;
(*p)->right = r;
p = &(r->left);
r = r->left;
}
if (l == NULL) *p = r;
Profesor Leopoldo Silva Bijit
20-01-2010
18
Estructuras de Datos y Algoritmos
else /* (r == NULL) */
*p = l;
return t;
}
static arbol lrot(arbol t)
{ arbol temp = t->right;
t->right = temp->left;
temp->left = t;
return ( temp);
}
static arbol rrot(arbol t)
{ arbol temp = t->left;
t->left = temp->right;
temp->right = t;
return (temp);
}
static void Error(int error, tipoclave valor)
{
if(error==1) printf("Error: Intenta insertar clave=%d existente!\n",valor);
else if(error==0) printf("Error: Intenta descartar clave=%d inexistente!\n", valor);
else if(error==2) printf("Error: Busca clave=%d en árbol vacío!\n", valor);
}
static nodo* CreaNodo(tipoclave valor)
{
arbol p;
p=(arbol)calloc(1, sizeof(nodo));
//p->nombre=(char*) NULL;
p->clave = valor;
p->left = NULL;
p->right = NULL;
return p;
}
static void LiberaNodo(arbol p)
{
//if (p->nombre != (char *)NULL) free(p->nombre);//libera string
free(p);
}
int AlturaArbol(arbol t)
{
if (t == NULL) return 0;
else return 1+max(AlturaArbol(t->left),AlturaArbol(t->right));
}
int ContarNodos(arbol t)
{
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
19
if (t == NULL) return 0;
else return 1+ContarNodos(t->left)+ContarNodos(t->right);
}
arbol BorraArbol(arbol t)
{
if (t != NULL) {
t->left=BorraArbol(t->left);
t->right=BorraArbol(t->right);
LiberaNodo(t);
}
return NULL;
}
/* end of splay.c */
13.7. Funciones para efectuar test de splay.
void ImprimeNodo(arbol t, int h)
{ int i;
for(i=0; i<h; i++) putchar('\t');
if(t==NULL) { putchar('*') ; putchar('\n') ;}
else printf("%d\n",t->clave);
}
void MuestraArbol(arbol t, int h)
{
if(t==NULL) ImprimeNodo(t, h);
else {MuestraArbol(t->right, h+1); ImprimeNodo(t, h); MuestraArbol(t->left, h+1);}
}
arbol insertarrecursivo(tipoclave valor, arbol T) /* recursivo */
{
if (T == NULL)
{ T = (arbol) malloc(sizeof(nodo));
if (T == NULL) printf("Rebalse del heap!\n");
else {T->clave = valor; T->left = T->right = NULL;}
}
else
if (valor < T->clave) T->left = insertarrecursivo(valor,T->left);
else if (valor > T->clave) T->right = insertarrecursivo(valor,T->right);
else Error(1,valor);
return(T);
}
#define maxnodos 2
arbol CreaArbol(arbol t, tipoclave a[])
{ int i;
Profesor Leopoldo Silva Bijit
20-01-2010
20
Estructuras de Datos y Algoritmos
for(i=0;i<maxnodos;i++)
if(insertarrecursivo(a[i],t)==NULL) printf("error en inserción\n");
return t;
}
/*Probar con:
*
*
4
4
4
*
/
\
/ \
*
2
6 2
6
*
/
*
1
*/
4
/ \
2
6
\
3
4
/ \
2
6
/
5
4
/ \
2
6
\
7
//Variables para mantener el árbol.
arbol root=NULL;
tipoclave arr[maxnodos]={2,6}; //orden de ingreso de claves
void main(void)
{
root=insertarrecursivo(4, root); //crea raíz
CreaArbol(root,arr); //agrega nodos
MuestraArbol(root, 1);
//comenzar test
//…………..
root=delarbol(root); //despedida limpia
}
13.8. Análisis de complejidad.
13.8.1 Objetivo del análisis amortizado.
La complejidad temporal de mantener un splay tree se suele analizar empleando Análisis de
amortizaciones.
La metodología está basada en la siguiente situación análoga: Una deuda bancaria genera
intereses todos los meses; la deuda puede pagarse mediante amortizaciones mensuales. Si la
amortización es mayor que los intereses la deuda disminuye; en caso contrario aumenta. Si se
decide aumentar la amortización en algunos períodos, en otros se la podrá disminuir.
En el caso de algoritmos, se puede “pagar más” (pre pagar) por una operación, los créditos
generados compensarán, más adelante operaciones que sean más costosas; lo que no se puede
hacer es ir aumentando la deuda.
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
21
El análisis amortizado se emplea para demostrar que el costo promedio de una operación es
pequeño, si se promedia sobre una serie de operaciones. A pesar de que una de las operaciones
de la serie pueda ser muy costosa.
Se busca encontrar el costo promedio de una operación en el peor caso.
El análisis de amortizaciones significa analizar costos promedios para una secuencia de
operaciones. Este acercamiento es razonable, ya que los análisis de complejidad temporal
basados en la operación de peor caso conducen a cotas pesimistas si la única forma de aceptar
una operación costosa es tener previamente un gran número de operaciones de bajo costo.
Tampoco es un análisis de un caso promedio, ya que en éstos se asumen que las entradas vienen
en determinados órdenes; lo que se efectuará es un promedio, en el tiempo, para una secuencia
de operaciones, sin asumir órdenes para los datos de entrada.
El método consiste en asignar un costo artificial para cada operación de la secuencia, este costo
artificial es denominado costo amortizado de una operación. La propiedad clave para asignar un
costo amortizado es que los costos reales de la secuencia de operaciones queden acotados por el
costo total amortizado de todas las operaciones. Entonces para analizar un algoritmo se pueden
emplear los costos amortizados, en lugar de los reales. La ventaja es que existe cierta
flexibilidad en la asignación de los costos amortizados.
13.8.2. Tipos de análisis.
Se tienen tres estilos de análisis.
Agregación:
Se demuestra que todas las secuencias de m operaciones tienen complejidad temporal O(g(n)),
entonces cada operación tendrá costo amortizado: O( g(n)/m) . Su dificultad para aplicarlo es
que normalmente el costo real de las operaciones depende fuertemente de los datos.
Balance: Se asocian créditos con los items en la estructura de datos.
A veces es difícil entender que son los créditos. Por ejemplo si las rotaciones generan créditos:
algunos nodos rotan más que otros, además los nodos pueden moverse en forma no predecible.
Se denomina también método del banquero.
Potencial: Se asocian créditos con la estructura completa de los datos.
Su principal dificultad es escoger una función potencial. Su elección no siempre es obvia, y
podría ignorar detalles de la estructura. A menudo puede intentarse elegirla, por ensayo y error;
a veces por intuición.
13.8.3. Analogía para la función potencial.
Primero consideremos una deuda bancaria, de valor D0, que se va pagando mediante
amortizaciones periódicas. Sea j el período; i la tasa de interés en el período j; a la
amortización; Dj, el valor de la deuda al finalizar el período j.
Profesor Leopoldo Silva Bijit
20-01-2010
22
Estructuras de Datos y Algoritmos
Entonces, al finalizar el período j, la deuda incurre en intereses, pero la deuda total se ve
disminuida en la amortización, se tiene entonces la relación:
D j 1 (1 i ) a
Dj
La deuda debe ir disminuyendo, hasta extinguirse.
D
Dj-1
D0
Dj
j
t
j
Figura 13.18. Amortizaciones.
D j 1i al costo real de los intereses, y cˆ j a la amortización realizada en el
Si denominamos c j
período j, tendremos:
cˆi
ci
Dj
1
Dj
Si definimos: D j
j
D0
Se tendrá, haciendo j=0, que: 0 = 0, y la relación puede escribirse, en términos de , que suele
denominarse función potencial, del siguiente modo:
cˆi
ci
j
j 1
Que es la relación que suele emplearse en el análisis de amortizaciones.
13.8.4. Ejemplo de análisis amortizado.
Incremento de un contador binario.
Se desea almacenar un gran contador binario en un arreglo A.
Todas las celdas comienzan en 0. La operación que se desea analizar es la de contar.
El algoritmo empleado es conmutar el bit A[0], si éste cambia de 1 a 0, se conmuta A[1]; y se
continua conmutando hasta que un bit cambia de 0 a 1. La tabla siguiente ilustra los cambios de
las componentes del arreglo, a medida que se cuenta en binario.
El costo de un incremento en la cuenta es el número de bits que cambian, esto se anota en la
última columna, y corresponde al costo real de pasar de una cuenta a la siguiente.
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
23
A[m] A[m-1] …. A[3] A[2] A[1] A[0] Costo
0
0
0
0
0
0
0
0
0
0
0
1
1
0
0
0
0
1
0
2
0
0
0
0
1
1
1
0
0
0
1
0
0
3
0
0
0
1
0
1
1
0
0
0
1
1
0
2
0
0
0
1
1
1
1
0
0
1
0
0
0
4
Nótese que en ocasiones cuesta poco el incremento y en otras cuesta más.
El número de bits que cambian cuando el incremento produce un número n es a lo más
1+floor(log n). Este es justamente el número de bits que se requieren para representar n en
binario. Por ejemplo, si n es 8, puede expresarse con 1+ParteEntera(log2(8)) = 4 bits.
Entonces, en una secuencia de n incrementos, el costo de peor caso por incremento está acotado
por n(1+floor(log n)) = O(n log n).
Calculemos costos amortizados, por los diferentes métodos.
Método de agregación.
Se observa que A[0] cambia en cada paso. Que A[1] cambia cada dos incrementos, que A[2]
cambia cada 4 incrementos, y así sucesivamente. Entonces si el costo total para conmutar A[0]
es n, el de conmutar A[1] será floor(n/2), y el costo total para conmutar A[2] será floor(n/4), etc.
El costo total será:
costo total = n + floor(n/2) + floor(n/4) + ...
costo total <= n + n/2 + n/4 + n/8 + ... <= 2n
Ya que la serie: 1+ ½ +1/4 + 1/8 +… tiende a 2.
Si el costo total de la secuencia de n operaciones de incremento es 2n, entonces esto implica que
el costo amortizado de un incremento es 2.
Método del banquero.
Por cada bit que es 1, en el contador, mantendremos un peso ($) en ese bit. Si la cuenta decimal
equivalente es 9 (1001 en binario), tendremos que tener un peso en el bit 0 y un peso en el bit 3.
Por otro lado, cada vez que se conmuta un bit, debemos pagar un peso para efectuarlo. Este es el
costo real.
Si pagamos dos pesos por la operación de incremento (este es el costo amortizado) veamos
cuánto cuesta realmente efectuar el incremento: En general se tiene una serie de bits menos
significativos que cambian de 1 a 0, y finalmente un bit cambia de 0 a 1, terminando el proceso.
Profesor Leopoldo Silva Bijit
20-01-2010
24
Estructuras de Datos y Algoritmos
Para los bits que cambian de 1 a 0, teníamos un peso asociado a ese bit, justamente para pagar
su bajada; y para el bit que cambia de 0 a 1, con los dos pesos de la amortización, pagamos su
cambio a 1, y a la vez dejamos el otro peso, asociado a ese bit, para uso futuro (pagamos por
adelantado, es decir estamos amortizando).
Si se asignan dos pesos por la operación de incremento (costo amortizado) esto garantiza que
siempre tendremos suficiente dinero para pagar por la operación, no importando cuánto cueste,
en forma real efectuar la operación de incremento.
Esto prueba que el costo amortizado de la operación incremento es dos.
Método del potencial.
En lugar de almacenar el dinero asociado a los elementos de la estructura, se puede
conceptualizar que el dinero total está asociado a la estructura completa de datos. El dinero total
almacenado, en el banco, se denomina función potencial.
Si escogemos:
(contador) = Número de unos en el contador.
Usando este formalismo, se define el costo amortizado de una operación. Si el sistema cambia
de estado D a D’ como resultado de una operación, se define:
Costo amortizado = costo real +
= costo real + (D’)- (D)
Es decir, el costo de la amortización debe ser el dinero que mantendremos en el banco y el
necesario para pagar por el trabajo.
Cálculo de costos amortizados:
Cuando los últimos m bits son unos, se tendrá: (D) = m.
El costo real para obtener la nueva cuenta será: m+1, ya que m+1 bits deben ser conmutados.
La nueva cuenta estará formada por un solo 1, en la posición m+1, entonces: (D’) = 1.
Se obtiene entonces:
Costo amortizado = (m + 1) +1 –m = 2
Si el potencial es siempre positivo, y comienza desde cero, como en el ejemplo, se tendrá, que
la suma de costos amortizados es una cota para la suma de los costos reales:
m
i 1
cˆi
m
(ci )
i 1
Puede comprobarse, que para cualquier operación incremento, el costo amortizado será dos.
Estudiaremos ahora cotas para la función potencial:
Para el caso inicial, de cuenta cero, tendremos: (inicial) = 0.
Si el contador es de 2 bits, después de 3 (22-1) operaciones, tendremos: (final) = 2.
Si el contador es de 3 bits, después de 7 (23-1) operaciones, tendremos: (final) = 3.
Si el contador es de n bits, después de (2n-1) operaciones, tendremos: (final) = n.
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
25
Si m es el número de operaciones: m= (2n-1), entonces: n = log2(m+1)
En la fórmula general, reemplazamos la suma de los costos amortizados por 2*m (ya que la
amortización es 2 por cada operación, y tenemos m operaciones); y la diferencia de potencial
por (0 –n) ; y a su vez expresamos n en función de m, se obtiene:
m
m
(cˆi )
ci
i 1
( Dinicial )
( D final ) = 2*m + (0 – n) = 2*m- log2(m+1)
i 1
m
ci = 2*m- log2(m+1) = O(m)
Costo real de m operaciones =
i 1
Es O(m) ya que m es mucho mayor que log(m) para valores grandes de m.
Esto comprueba que el costo amortizado de una operación es O(m/m) = O(1), es decir,
constante.
13.8.5. Definiciones.
Para aplicar el método del potencial, al análisis de algoritmos, efectuemos algunas definiciones.
Consideremos una secuencia de m operaciones op1 , op2 ,… , opm.
Asumiremos que la estructura de datos tiene una función potencial. La función potencial puede
pensarse como el pago de una cuenta bancaria, como se mencionó antes.
Di = Estado de la estructura de datos después de la operación i-ésima
(Di) =Potencial (i.e., créditos) de la estructura completa Di.
ci = Costo actual de operación i-ésima, lo que corresponde a los intereses. Cada operación opi ,
tiene un costo proporcional a su tiempo de ejecución.
Se pagan los costos ci , de las operaciones opi mediante amortizaciones cˆi , entonces:
cˆi
ci
( Di )
( Di 1 ) = Costo amortizado de operación i-ésima
La diferencia entre el costo real y las amortizaciones se carga al potencial de la estructura. Se
aumenta (invierte o prepaga) el potencial si los costos amortizados son mayores que los costos
actuales, en caso contrario el potencial disminuye.
( Di 1 ) es positiva el costo amortizado representa un
Si la diferencia de potencial ( Di )
sobrepago de la operación i-ésima, ya que el potencial aumenta.
Como se estableció antes, lo que interesa es el costo promedio en una secuencia de operaciones,
entonces para m operaciones:
m
i 1
cˆi
m
(ci
( Di )
( Di 1 ))
i 1
Pero la diferencia de potencial es serie telescópica, y se puede escribir:
Profesor Leopoldo Silva Bijit
20-01-2010
26
m
Estructuras de Datos y Algoritmos
cˆi
i 1
m
(ci )
( Dm )
( D0 )
i 1
La cual permite establecer que:
m
cˆi
i 1
m
(ci ) si ( Dm )
( D0 ) entonces el costo amortizado total será una cota superior del
i 1
costo real.
Si se logra tener una función potencial que cumpla con ( D0 ) 0 (potencial de referencia) y
como el número de operaciones que se realizarán no suele conocerse por adelantado, entonces
lo que basta demostrar es que ( Di ) 0 para todo i; de esta forma se asegura que el costo
m
amortizado será una cota para el costo real. Es decir:
cˆi
i 1
m
(ci ) con ( Dm ) 0 para todos
i 1
los valores de m.
Entonces: Se paga por la secuencia de operaciones op1 , op2 ,… , opm no más que la disminución
n
de potencial más la suma de los costos de las amortizaciones
cˆi .
i 1
El costo real se calcula sumando al costo amortizado la diferencia de potencial:
m
m
ci
i 1
(cˆi )
( D0 )
( Dm )
i 1
13.8.6. Pasos en la aplicación del método del potencial.
En un caso general, el potencial puede ser negativo y no comenzar de cero. Lo que debe
observarse son los potenciales inicial y final.
En el método del potencial, se suelen realizar tres pasos:
Primero escoger una función potencial adecuada (esto es casi arte). Segundo, empleando la
función potencial acotar el costo amortizado de la secuencia de operaciones en que se está
( D final ) , para obtener una cota para los
interesado. Y tercero, es preciso acotar ( Dinicial )
costos reales.
Puede notarse que si el costo actual de una operación es elevado, y si se desea pagar una
amortización baja, entonces el potencial debe disminuir bastante para efectuar la cancelación.
Para el caso de los árboles splay, el análisis está basado en definir una función potencial, y
demostrar que cada operación splay tiene costo amortizado O( log(n) ), si esto es así, se puede
concluir que la secuencia completa tiene costo O (m log (n) ) .
En árboles balanceados, se tiene O(log n) por operación, y O(m log n) para m operaciones.
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
27
En árboles splay, como se verá, se podría tener O(n) para alguna operación en peor caso, y O(m
log n) como costo promedio para la secuencia de m operaciones.
Puede definirse, en general, que si todas las secuencias de m operaciones tienen costo O(mf(n)),
entonces cada operación tiene costo amortizado O(f(n)) .
13.8.7. Función potencial en árboles splay.
Algunas consideraciones para encontrar una función potencial.
En árboles splay se rotan todos los nodos visitados en un acceso, esto se logra con un costo
constante adicional asociado a la rotación. Pero las rotaciones tienden a balancear el árbol, y
además los nodos accesados son movidos a la raíz.
Sin embargo las reglas heurísticas asociadas a las rotaciones no tienen beneficios obvios. Las
reglas para las rotaciones en árboles splay se obtuvieron ensayando diversas heurísticas, hasta
encontrar un conjunto que redujera los costos amortizados.
Para un árbol T, se puede definir la función tamaño s(i) como la suma de pesos positivos
arbitrarios w(i), asignados a cada nodo, de todos los nodos del árbol Ti, que tienen al i como
raíz:
s (i )
w(i )
i Ti
Por ejemplo puede definirse w(i) como q(i), el número de accesos al nodo i, partido por el
número de operaciones splay, realizadas sobre la estructura.
n
q (i )
Sea w(i) = q(i)/m. Nótese que en este caso:
m
i 1
Esta elección de los pesos permite obtener cotas más adecuadas en la ejecución de casos reales.
Si cada nodo es accesado sólo una vez: w(i) sería el número de descendientes del nodo i, con
éste incluido, partido por el número de nodos en el árbol.
La elección de los pesos para cada nodo nos permitirá acotar los costos reales de una secuencia
de operaciones.
Sea además la función rango r(i) = floor(log2S(i)). Lo cual permite rangos enteros.
Se define el potencial según:
(T )
floor (log 2 s(i ))
i T
r (i )
i T
La función floor(x) da el mayor entero menor o igual a su argumento. Es decir, la parte entera.
Se obtiene truncando el número real.
Profesor Leopoldo Silva Bijit
20-01-2010
28
Estructuras de Datos y Algoritmos
Ejemplo función potencial.
Efectuaremos una asignación simple de pesos, todos con valor 1. En este caso s(i) es la suma de
nodos pertenecientes al árbol cuya raíz es i. Calcularemos el potencial de la estructura en dos
situaciones. La primera un árbol bastante desbalanceado, al cual mediante una operación splay,
se lleva el nodo con valor 4 a la raíz.
7-2
6-2
7
6
3-1
5-2
4-2
1-0
7-2
4
6
2
3-1
5
1
3
5
7
1-0
1-0
1-0
1-0
2
4
1
2-1
s(i)-r(i)
3
1-0
Figura 13.19. Tamaños y rangos de los nodos.
En las figuras se han anotado los valores de s(i) y r(i) para cada nodo.
Notar que s(4)=2 y r(4)=1 en la figura izquierda; y que s(4)=7 y r(4) =2 en la de la derecha. Los
nodos más alejados de la raíz tienen rangos menores; a medida que ascienden, aumentan su
rango; y el rango de las hojas es 0.
Para la figura a la izquierda, la estructura inicial, el potencial es:
(T
D0 ) log(1) log(1) log(2) log(4) log(5) log(6) log(7) 9
Para la figura a la derecha, después de las dos rotaciones, el potencial es:
(T
D2 ) log(1) log(1) log(1) log(1) log(3) log(3) log(7)
4
Para llegar desde la figura a la izquierda a la de la derecha, se requiere efectuar una operación
Zig-Zag, y una operación Zig-Zig. Ambas operaciones tienen costo real de 2 rotaciones, cada
una.
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
29
Puede verse que la elección de esta función potencial (con todos los pesos iguales a 1) y
definiendo el rango como logaritmo produce un potencial cercano a n para árboles balanceados,
y nlog(n) para árboles muy desbalanceados.
Si consideramos los r(i) como pesos asociados al nodo, en el caso del método del banquero,
puede apreciarse que se requiere invertir poco en nodos muy alejados de la raíz. Ya que las
operaciones los llevaran a rangos mayores. Otra forma de verlo, es que debe tenerse mucha
plata en el banco si el árbol, está muy desbalanceado.
Se dispone de (9-4)=5 pesos en la estructura para un costo real de 4 rotaciones. Si se efectúa una
amortización, ésta aumentará el potencial de la estructura.
La ventaja de esta definición, es que en general, a lo más tres nodos cambian su función s (y por
lo tanto r): El abuelo, el padre y el nieto. Estos cambios pueden calcularse, como se verá a
continuación.
13.8.8 Cálculo de costos amortizados por operación.
Antes de la operación, se tienen, para el nodo x: r(x), s(x)
Después de la operación, para el nodo x, se tienen:
r’(x), s’(x)
Se desea demostrar que los costos amortizados para cada operación individual quedan acotados
según:
Si x es la raíz: ĉi = 1
Zig:
ĉi 3(r’(x) - r(x)) + 1
Zig-zig:
ĉi 3(r’(x) - r(x))
Zig-zag:
ĉi 3(r’(x) - r(x))
Se calculan las cotas para los costos amortizados por operación, contando cada rotación como
un costo real O(1) y calculado la diferencia de potencial, antes y después de la operación.
Caso: Nodo x es la raíz
No cambia la función potencial, los nodos permanecen con sus mismos rangos individuales. El
costo de encontrar al nodo en la raíz es constante, y se le da valor 1.
Entonces:
cˆi ci
( Di ) ( Di 1 ) = 1 + 0 = 1
Caso: Zig
y
x
x
C
y
A
A
B
B
C
Figura 13.20. Cambios de tamaños y rangos en Zig.
Profesor Leopoldo Silva Bijit
20-01-2010
30
Estructuras de Datos y Algoritmos
El costo real es una rotación.
cˆi ci
( Di ) ( Di 1 ) = 1 +( r'(x) + r'(y) ) - (r(y) + r(x)) sólo x e y cambian su rango.
Se tiene: s(y) > s’(y) y s’(x)> s(x) y mediante logaritmos se obtienen: r(y)> r’(y), y
r’(x)>r(x).
Entonces se puede acotar la amortización según:
cˆi <= 1 + r'(x) - r(x) ya que r’(y)-r(y) < 0
Entonces con mayor razón, ya que r’(x)-r(x)>0:
cˆi <= 1+ 3( r'(x) - r(x) )
Caso: Zig-Zag
z
x
y
z
y
D
x
A
A
B
B C
D
C
Figura 13.21. Cambios de tamaños y rangos en Zig-Zag.
Se tienen: s’(x) = s(z), s(y)>=s(x) , s’(x)>=s’(y)+s’(z)
La primera implica: r’(x)-r(z)=0
La segunda: r(y)>=r(x)
Para la tercera, se emplea la relación:
c a b, a 0, b 0
log(a) log(b) 2log(c) 2 Los logaritmos son en base dos.
La que se demuestra, según:
0 ( a b) 2
a 2 2ab b 2 Un cuadrado siempre es positivo
4ab a 2 2ab b 2
( a b) 2
c 2 Sumando 4ab en ambos lados, se obtiene
c2
ab
4
log(a) log(b) 2 log(c) 2
Entonces a partir de s’(x)>=s’(y)+s’(z), se obtiene:
r’(y)+r’(z) <=r’(x)-2 la que implica: 2 <= 2r'(x) - r'(y) - r'(z)
Calculando ahora el costo amortizado de Zig-Zag:
cˆi ci
( Di ) ( Di 1 ) . Se requieren dos rotaciones, entonces el costo real es 2.
cˆi = 2 + ( r’(x)+r’(y)+r’(z) ) - ( r(x)+r(y)+r(z) ) ordenando, se obtiene:
cˆi = 2 + r’(x)-r(z) –(r(x)+r(y)) + r’(y) + r’(z)
Acotando la amortización, reemplazando r'(x) = r(z) y r(y) = r(x)+a, con a>0, se obtiene:
cˆi <= 2 - 2r(x) + r'(y) + r'(z) , reemplazando ahora el primer 2, por algo mayor, se tiene:
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
31
cˆi <= (2r'(x) - r'(y) - r'(z)) + r'(y) + r'(z) - 2r(x), simplificando, se obtiene:
cˆi <=2r’(x)-2r(x) y con mayor razón, ya que 3 es mayor que 2, y r’(x)>=r(x):
cˆi <=3 ( r'(x) - r(x) )
Caso: Zig-Zig.
x
z
y
y
A
D
z
x
B
C
A
C
B
D
Figura 13.22. Cambios de tamaños y rangos en Zig-Zig.
Se tienen;
s'(x) = s(z) , s'(x) >= s'(y), s(y) >= s(x), s(x) + s'(z) <= s'(x)
La última refleja que todos los nodos bajo x, luego de la rotación, deben ser al menos la suma de
todos los nodos del árbol.
Calculando ahora el costo amortizado de Zig-Zig, que también tiene costo real de dos
rotaciones:
cˆi ci
( Di ) ( Di 1 )
cˆi = 2 + ( r’(x)+r’(y)+r’(z) ) - ( r(x)+r(y)+r(z) )
cˆi = 2 + ( r’(y)+r’(z) ) - ( r(x)+r(y) ) usando en la anterior que r'(x) = r(z)
Buscando acotar la amortización, se tiene:
cˆi <=2 + r'(x) + r'(z) - 2r(x) usando r'(x) >= r'(y) y r(y) >= r(x)
Usando log(a) log(b) 2log(c) 2 , con s(x) + s'(z) <= s'(x) se tiene:
r(x)+r’(z)<=2r’(x) -2 la que puede escribirse:
2 <= 2r'(x) - r(x) - r'(z) , lo cual puede reemplazarse en el costo amortizado por el primer 2.
Se obtiene:
cˆi <= (2r'(x) - r(x) - r'(z) ) + r'(x) + r'(z) - 2r(x) y arreglando, finalmente.
cˆi <= ( 3r'(x) - 3r(x) )
Ejemplo
Veamos un cálculo amortizado para splay(3, root)
Profesor Leopoldo Silva Bijit
20-01-2010
32
Estructuras de Datos y Algoritmos
1
1
1
2
3
2
3
6
6
Zig-Zig
5
2
Zag-Zig
3
4
4
3
1
6
6
2
Zag
4
4
5
5
5
Figura 13.23. Cálculos amortizados.
Sea x el nodo con valor 3, entonces:
La operación Zig-Zig tiene costo amortizado menor que: 3(r’(x) - r(x) )
La operación Zag-Zig tiene costo amortizado menor que: 3(r’’(x) - r’(x) )
La operación Zag tiene costo amortizado menor que:
3(r’’’(x) - r’’(x)) + 1
Costo total:
3
cˆi <= 3(r’(x) - r(x) ) + 3(r’’(x) - r’(x) ) + 3(r’’’(x) - r’’(x)) + 1
i 1
Debido a la suma telescópica se cancelan r’(x) y r’’(x). Además la última rotación deja al nodo
en la raíz, por lo tanto:
3
cˆi <= 3(r’’’(x) –r(x)) +1 = 3( r(raíz) – r(x) ) +1
i 1
13.8.9. Costo total amortizado de una operación splay.
El costo total amortizado de una operación splay, de un nodo x en un árbol cuya raíz es t, será
debido a las sumas telescópicas: 3(r(t)-r(x)) +1
Lo cual puede escribirse:
3(r (t ) r ( x)) 1 3log(
s(t )
s(t )
n
) 1 O(log(
)) O(log( ))
s ( x)
s ( x)
p
Ya que s(t) es n, el número de nodos en el árbol; y s(x) = p, donde p<n es el número de nodos
en subárbol con raíz x, incluida la raíz x. Si el nodo x, es hoja, entonces p=1, y el costo total
amortizado será: O(log(n)).
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
33
Las operaciones insertar, buscar y descartar difieren sólo en un factor constante de la operación
splay, ya que se implementan en función de ésta.
13.8.10. Cambios de potencial.
El mayor cambio de potencial se produce cuando un nodo se mueve desde la raíz a una hoja.
En el peor caso el potencial inicial está asociado a una lista.
(T
D0 ) log(1) log(2) log(3) log(4)... log( n) log( ( n 1)) O(n log(n))
En el peor caso el potencial final tiene valor cero. En el cual el árbol está formado por una sola
hoja.
Entonces la mayor diferencia de potencial se tiene para:
( D0 )
( Dm ) O(n log(n)) 0
13.8.11. Teorema de Balance.
Una secuencia de m operaciones splay en un árbol de n nodos tiene una complejidad temporal:
O(m log(n) + n log(n)).
Demostración:
Con r(t) <= log(n) y r(x) >=0 se obtiene el costo amortizado de una operación: 3log(n) 1
Entonces, las m operaciones, más el cambio de potencial, se expresa según:
m(3log(n) 1) O(n log(n)) la cual es O(m log(n) + n log(n)).
Completando la demostración.
Referencias.
Daniel Sleator, Robert Tarjan, “Self-Adjusting Binary Search Trees”, Journal of the Association
for Computing Machinery. Vol. 32, No. 3, July 1985, pp. 652-686.
Profesor Leopoldo Silva Bijit
20-01-2010
34
Estructuras de Datos y Algoritmos
Índice general.
CAPÍTULO 13 ............................................................................................................................................1
ÁRBOLES DESPLEGADOS. SPLAY TREES.......................................................................................1
13.1 DEFINICIÓN. ......................................................................................................................................1
13.2 OPERACIÓN SPLAY. ...........................................................................................................................1
Zig. .......................................................................................................................................................2
Zig-Zag.................................................................................................................................................2
Mover a la raíz. ....................................................................................................................................3
13.3 TIPOS DE ALGORITMOS. .....................................................................................................................3
13.3.1. Splay Bottom-up. ......................................................................................................................3
13.3.2. Ejemplos de operaciones splay bottom-up. .......................................................................................... 4
Splay(3, root); ............................................................................................................................................. 4
Splay(1, root); ............................................................................................................................................. 4
MuevealaRaiz(1, root)................................................................................................................................. 5
Insertar nodo con valor 5. ............................................................................................................................ 5
Descartar(4, root). ....................................................................................................................................... 6
Descartar(6, root) ........................................................................................................................................ 6
13.3.2. Splay top-down.........................................................................................................................7
Zig-Zig ............................................................................................................................................................. 8
Zig-Zag. ........................................................................................................................................................... 9
Join................................................................................................................................................................... 9
Ejemplo top-down. ......................................................................................................................................... 10
13.4. ANIMACIONES. ...............................................................................................................................11
13.5. CÓDIGOS. .......................................................................................................................................11
13.6. OPERACIONES UTILITARIAS. ...........................................................................................................17
SE AGREGA DESCARTAR PARA COMPLETAR LAS OPERACIONES BÁSICAS ........................................................17
13.7. FUNCIONES PARA EFECTUAR TEST DE SPLAY. .................................................................................19
13.8. ANÁLISIS DE COMPLEJIDAD. ...........................................................................................................20
13.8.1 Objetivo del análisis amortizado.............................................................................................20
13.8.2. Tipos de análisis.....................................................................................................................21
13.8.3. Analogía para la función potencial. .......................................................................................21
13.8.4. Ejemplo de análisis amortizado. ............................................................................................22
Método de agregación. ................................................................................................................................... 23
Método del banquero. .................................................................................................................................... 23
Método del potencial. ..................................................................................................................................... 24
13.8.5. Definiciones. ..........................................................................................................................25
13.8.6. Pasos en la aplicación del método del potencial. ..................................................................26
13.8.7. Función potencial en árboles splay. ......................................................................................27
Ejemplo función potencial. ............................................................................................................................ 28
13.8.8 Cálculo de costos amortizados por operación. .......................................................................29
Caso: Nodo x es la raíz .................................................................................................................................. 29
Caso: Zig ........................................................................................................................................................ 29
Caso: Zig-Zag ................................................................................................................................................ 30
Caso: Zig-Zig. ................................................................................................................................................ 31
Ejemplo .......................................................................................................................................................... 31
Profesor Leopoldo Silva Bijit
20-01-2010
Árboles desplegados. Splay trees.
35
13.8.9. Costo total amortizado de una operación splay. ................................................................... 32
13.8.10. Cambios de potencial. ......................................................................................................... 33
13.8.11. Teorema de Balance. ........................................................................................................... 33
REFERENCIAS. ........................................................................................................................................ 33
ÍNDICE GENERAL. ................................................................................................................................... 34
ÍNDICE DE FIGURAS................................................................................................................................. 35
Índice de figuras.
FIGURA 13.1. OPERACIÓN ZIG. ..................................................................................................................... 2
FIGURA 13.2. OPERACIÓN ZIG-ZIG. .............................................................................................................. 2
FIGURA 13.3. OPERACIÓN ZIG-ZAG. ............................................................................................................. 2
FIGURA 13.4. MOVER X HACIA LA RAÍZ. ....................................................................................................... 3
FIGURA 13.5. OPERACIÓN SPLAY(3, ROOT)................................................................................................... 4
FIGURA 13.6. OPERACIÓN SPLAY(1, ROOT)................................................................................................... 4
FIGURA 13.7. OPERACIÓN MOVER A LA RAÍZ................................................................................................ 5
FIGURA 13.8. OPERACIÓN INSERTAR(5, ROOT) ............................................................................................. 5
FIGURA 13.9. OPERACIÓN DESCARTAR(4, ROOT).......................................................................................... 6
FIGURA 13.10. OPERACIÓN DESCARTAR(6, ROOT)........................................................................................ 6
FIGURA 13.11. TOP-DOWN ZIG ..................................................................................................................... 8
FIGURA 13.12. TOP-DOWN ZIG-ZIG .............................................................................................................. 8
FIGURA 13.13. TOP-DOWN ZIG-ZAG ............................................................................................................. 9
FIGURA 13.14. TOP-DOWN JOIN. ................................................................................................................... 9
FIGURA 13.15. TOP-DOWN ZIG-ZIG EN C. ................................................................................................... 10
FIGURA 13.16. TOP-DOWN ZIG-ZAG EN E. .................................................................................................. 10
FIGURA 13.17. TOP-DOWN JOIN. ................................................................................................................. 11
FIGURA 13.18. AMORTIZACIONES. .............................................................................................................. 22
FIGURA 13.19. TAMAÑOS Y RANGOS DE LOS NODOS. .................................................................................. 28
FIGURA 13.20. CAMBIOS DE TAMAÑOS Y RANGOS EN ZIG........................................................................... 29
FIGURA 13.21. CAMBIOS DE TAMAÑOS Y RANGOS EN ZIG-ZAG. ................................................................. 30
FIGURA 13.22. CAMBIOS DE TAMAÑOS Y RANGOS EN ZIG-ZIG. .................................................................. 31
FIGURA 13.23. CÁLCULOS AMORTIZADOS. ................................................................................................. 32
Profesor Leopoldo Silva Bijit
20-01-2010
Descargar