Está en la página 1de 19

1

Capítulo 16.

Árboles de búsqueda aleatorizados.


Treaps.

Cuando los ítems almacenados en un árbol de búsqueda binario cambian en el tiempo, debido a
que éstos pueden ser insertados o descartados de manera no predecible, no puede asegurarse que
todas las hojas mantengan profundidades similares. Debido a esto se han desarrollado diversos
algoritmos determinísticos para lograr mantener una estructura balanceada: Árboles AVL,
árboles 2-3, árboles coloreados, árboles AA y muchos otros; todos ellos modifican la estructura
basados en pequeñas operaciones locales denominadas rotaciones y en un análisis cuidadoso de
las diferentes y determinadas situaciones que se producen.

Antes se demostró que un árbol generado aleatoriamente tiene una altura esperada que varía
logarítmicamente con respecto al número de nodos almacenados en la estructura, y que tiende a
ser balanceado.

Se han desarrollado algoritmos que introducen aleatoriedad en las operaciones de actualización


y búsqueda en un árbol binario, con el objetivo de mantener el árbol balanceado. La distribución
de probabilidades no proviene de las claves sino de las elecciones aleatorias que realiza el
algoritmo; por esto se denominan algoritmos aleatorizados. Para analizar el costo de un
algoritmo usando probabilidades, es preciso conocer la distribución de las entradas, pero en
muchas situaciones se conoce muy poco o no es posible modelar la distribución de éstas; sin
embargo puede lograrse una conducta aleatoria en el algoritmo.

Más específicamente se denominan aleatorizados a aquellos algoritmos cuyo comportamiento


queda determinado no sólo por sus entradas sino por valores producidos por un generador de
números aleatorios.

Se estudiará uno de estos métodos que consiste en introducir un número aleatorio en cada nodo.
El número aleatorio será la prioridad del nodo. Las claves y las prioridades deben ser diferentes
y pertenecer a un universo totalmente ordenado. En el árbol, las claves están almacenadas según
un árbol binario de búsqueda, y las prioridades según una cola de prioridad. Por esta razón a la
estructura se la denomina treaps que es un acrónimo de trees y heaps.

Si las claves y las prioridades son diferentes para un conjunto determinado de valores de claves
y prioridades el treap es único; y su forma correspondería al árbol binario de búsqueda que se
obtendría al insertar, a partir de un árbol vacío, los ítems en orden de prioridad creciente.

Profesor Leopoldo Silva Bijit 26-05-2008


2 Estructuras de Datos y Algoritmos
Un treap aleatorio también es el resultado de insertar las claves en orden aleatorio, en un árbol
binario de búsqueda, lo cual puede lograrse si antes de insertar una clave se le asigna una
prioridad generada aleatoriamente.

Lo notable de esta estructura es que está basada en dos de las fundamentales que se estudian en
un curso básico de estructuras de datos: el árbol binario de búsqueda y una cola de prioridad o
heap. Si se tuviera una función de hash que genere un valor aleatorio (la prioridad), a partir de
la clave, no sería necesario almacenar el valor de prioridad en cada nodo, reduciendo el tamaño
del almacenamiento, ya que mediante la función pueden calcularse las prioridades de los nodos
cuando sea necesario disponer de ellas. En esta forma de diseño, a partir de tres estructuras
fundamentales de datos se construye una nueva.

16.1. Análisis de las operaciones.

Si se inserta un nodo con clave determinada, de manera usual, en las hojas de un árbol binario,
se tiene que todas las claves de los nodos cumplen la propiedad de un árbol binario de
búsqueda; sin embargo puede ser que la propiedad del heap, que es tener prioridades de los hijos
mayores que el padre, no se cumpla. Para reestablecer esa propiedad, al nodo recién insertado se
lo hace ascender, mediante rotaciones, mientras el padre tenga prioridad mayor. El proceso se
detiene si se encuentra un padre con prioridad menor o si el nuevo nodo se convierte en la raíz.

El descarte de un nodo se logra invirtiendo las operaciones que se realizan para insertarlo; es
decir, una vez ubicada la clave, se hace descender al nodo, mediante rotaciones con el nodo con
menor prioridad de sus hijos, hasta que el nodo sea una hoja; instancia en que se lo puede
descartar, preservándose las propiedades de los árboles binarios de búsqueda y de los heaps.

La Figura 16.1, ilustra un treap, donde en la parte superior se muestra la clave y en la inferior de
cada nodo la prioridad. El árbol se formó ingresando las claves de acuerdo a un orden creciente
de las prioridades; es decir en orden: 10, 12, 13, 18, 22, 24, 33 y 40. El nodo con menor valor
de prioridad se ubica en la raíz.
9
10

4 15
12 13

2 5 12 20
22 24 18 40

7
33

Figura 16.1. Treap.

Si se inserta un nodo con clave 8 y prioridad 11, de acuerdo al algoritmo de inserción debe
agregárselo a la derecha del nodo con clave 7. Esto se muestra a la izquierda de la Figura 16.2.
Como no se cumple la propiedad que la prioridad del nodo 8 sea mayor que la de su padre,

Profesor Leopoldo Silva Bijit 26-05-2008


Árboles de búsqueda aleatorizados. Treaps. 3
puede reestablecerse esta condición mediante la rotación a la izquierda en el nodo padre, el 7 en
este caso. Lo cual se muestra a la derecha de la Figura 16.2.
9 9
10 10

4 15 4 15
12 13 12 13

2 5 12 20 2 5 12 20
22 24 18 40 22 24 18 40

7 8
33 11

8 7
11 33

Figura 16.2. Inserción de clave 8 en Treap.

Del nodo con clave 8 hacia abajo se tiene un heap, pero no hacia arriba. Por esto es preciso
efectuar otra rotación a la izquierda en el nodo padre del 8, el nodo con clave 5 en la Figura 16.2
a la derecha. Luego de realizada, se obtiene el diagrama a la izquierda de la Figura 16.3.
Finalmente volviendo a rotar a la izquierda en el nodo con clave 4, padre del nodo con clave 8,
se logra el treap a la derecha de la Figura 16.3.
9 9
10 10

4 15 8 15
12 13 11 13

2 8 12 20 4 12 20
22 11 18 40 12 18 40

5 2 5
24 22 24

7 7
33 33

Figura 16.3. Inserción de clave 8 en Treap.

El descarte del nodo con clave 8, sigue el proceso inverso a su inserción, se lo hace descender,
mediante rotaciones con el nodo hijo que tenga menor prioridad, hasta llegar a ser una hoja.

La operación descarte se ve simplificada si los enlaces derecho e izquierdo de las hojas apuntan
a un nodo de fondo o centinela que tenga un valor mayor de prioridad que los valores de
prioridad que puedan tener los nodos.
Entonces los condicionales, que deberían efectuarse cerca de las hojas, para asegurar la
existencia de los valores p->left->prioridad o p->right->prioridad no son necesarios.

Profesor Leopoldo Silva Bijit 26-05-2008


4 Estructuras de Datos y Algoritmos

La operación de inserción debe descender hasta las hojas y luego ascender preservando el heap.
Esto puede lograrse de manera simple mediante un diseño recursivo, o empleando un stack, para
almacenar la ruta de descenso; una vez ubicada la posición para insertar, al retornar de las
invocaciones recursivas, se dispone del nodo padre, en el cual deben realizarse las rotaciones
que corresponda.

La operación de descarte, debe descender hasta encontrar el nodo que se busca para descartar, y
luego seguir descendiendo hasta las hojas, manteniendo el heap. Para lograrlo, de manera
iterativa, es necesario mantener en el descenso un puntero al padre del nodo que está
descendiendo para ser descartado, además de la dirección de descenso; ya que es preciso
mantener los enlaces. Lo anterior también puede simplificarse si los nodos contienen un puntero
al padre, pero esto aumenta el tamaño del almacenamiento del nodo.

Si la función que genera aleatoriamente las prioridades, produjera algunos números con iguales
valores, los algoritmos de inserción y descarte se realizan normalmente. En la inserción no se
realizan rotaciones adicionales, y quedarían elementos con prioridades iguales adyacentes,
alargando la altura del último nodo ingresado; pero si esto ocurre con baja probabilidad puede
tolerarse. En el descarte, si la prioridad del nodo es igual a la de uno de sus hijos, disminuye el
largo de secuencias de igual prioridad y mejora el balance; por otro lado si los hijos tienen
iguales prioridades, tiende a aumentar la altura del último nodo. Por lo tanto es perfectamente
tolerable aceptar un generador aleatorio simple que demande bajo número de operaciones y que
produzca un limitado número de colisiones en las prioridades.

En un árbol binario de búsqueda, si la raíz es la clave menor, entonces no hay subárbol


izquierdo; así también si la raíz es el nodo con la mayor clave del conjunto no habrá subárbol
derecho. Obviamente en estos casos se tiene un árbol desbalanceado. Pero si los valores de
prioridad son estadísticamente independientes de los valores de las claves es poco probable que
la raíz, que tiene prioridad mínima, sea coincidente con las claves de valores máximo o mínimo.
De este modo el número de nodos ubicados a la derecha pueden ser similares al número de
nodos ubicados a la izquierda; lo cual genera una estructura con cierto grado de balance.

El algoritmo aleatorizado fuerza una distribución de probabilidades en las entradas asegurando


que ninguna combinación particular de éstas producirá un peor caso de comportamiento. En
árboles de búsqueda, las elecciones aleatorias de las prioridades impiden que largas secuencias
de claves en orden ascendente o descendente provoquen alargues de algunas ramas del árbol.

Los autores del algoritmo diseñan recursivamente ambas operaciones de actualización. También
demuestran que el valor esperado de la altura de un treap, con n nodos, es O(log(n)) , y que el
valor esperado de las rotaciones necesarias para mantener el treap es menor que 2; es decir un
número constante.

También muestran que una función de hash adecuada, para generar las prioridades, a partir de
una clave i, es un polinomio de grado 4.

q(i) = ((((c4*i + c3)*i+ c2)*i +c1)*i+c0) mod U

Profesor Leopoldo Silva Bijit 26-05-2008


Árboles de búsqueda aleatorizados. Treaps. 5

Donde las constantes deben ser números aleatorios entre 0 y (U-1). Con U un número primo
mayor que n3 , con n elementos en el árbol; lo cual logra asegurar que dos elementos pueden
tener prioridades iguales con una probabilidad menor que 1/n.

16.2. Complejidad.

16.2.1. Conceptos básicos de probabilidad.

Una variable aleatoria indicadora I (e) asociada con el evento e de un espacio muestreal S, está
definida según:
I (e) =1 si ocurre el evento e, y 0 si el evento e no ocurre.

Se define el valor esperado como el promedio de los valores que la variable puede tomar. En un
espacio discreto, es la suma ponderada, de acuerdo a su probabilidad, de todos los valores que
puede tomar la variable X:
E[ X ] x Pr( X x)
x

El valor esperado de la variable aleatoria indicadora Ve I (e) se calcula de acuerdo a su


definición, y considerando que sólo puede tomar dos valores, según:

E[Ve ] E[ I (e)] 1 Pr(e) 0 Pr(e ) Pr(e) Pr( I 1)

Donde e simboliza el complemento de e en el espacio S; es decir: S-e.

Las variables aleatorias indicadoras son útiles para analizar situaciones en las que se realizan
repetidos ensayos aleatorios.
Si se emplea la variable indicadora Vi para asociarla con la producción del evento e en el
ensayo i-ésimo. Entonces la variable aleatoria V que indica el número total de eventos e en los n
ensayos, queda descrita por:
i n
V Vi
i 1
Y el valor esperado de V, se logra tomando la expectación en ambos lados de la expresión
anterior:
i n
E[V ] E[ Vi ]
i 1
Pero el operador expectación es lineal, y se tendrá que el valor esperado de la suma de dos
variables aleatorias es la suma de sus expectaciones, esto aún si las variables no son
independientes. Para el caso de la sumatoria anterior, se tiene:
i n
E[V ] E[Vi ]
i 1

Profesor Leopoldo Silva Bijit 26-05-2008


6 Estructuras de Datos y Algoritmos
Pero el valor esperado de una variable aleatoria indicadora es simplemente su probabilidad,
entonces se tiene:
i n
E[V ] Pr(Vi 1)
i 1
Este último resultado es útil en el cálculo de complejidades en algoritmos aleatorizados, como
se verá a continuación.

16.2.2. Altura.

Sea xk el nodo que tiene la k-ésima clave menor en el treap; y ai ,k una función que vale 1 si xi es
ancestro propio de xk , y 0 en caso contrario.
Entonces el número de nodos del trayecto de xk a la raíz, está dada por:
i n
h( xk ) ai ,k
i 1

Que es la suma de los ancestros propios de xk .


El valor esperado para la altura del nodo xk , equivale a calcular la probabilidad de que un nodo i
sea un ancestro propio del nodo k.
i n
h E (h( xk )) Pr(ai ,k 1)
i 1
Para efectuar el cálculo definiremos un subconjunto ordenado de nodos del treap:

X (i, k ) {xi , xi 1 ,.., xk } si i < k;


X (i, k ) {xk , xk 1 ,.., xi } si i > k;

El subconjunto anterior contendrá exactamente: | k i | 1 nodos.

La Figura 16.4, ilustra la situación de los nodos del conjunto. Sin perder generalidad se ha
supuesto que las claves están formadas por números entre 1 y n. El diagrama a la derecha
muestra un caso en que i < k , y el izquierdo un caso en que i > k.
i i
i+1 i+1

k k

Figura 16.4. Subconjunto de nodos en un treap.

Debido a la construcción del treap, puede asegurarse que xi es un ancestro propio de xk si y sólo
si xi tiene la menor prioridad entre todos los nodos en el subconjunto X (i, k ) .

Profesor Leopoldo Silva Bijit 26-05-2008


Árboles de búsqueda aleatorizados. Treaps. 7
Demostración:
a) Si xi es la raíz, entonces es un ancestro de xk , y por ser la raíz tiene la menor prioridad que
cualquier nodo del treap, y por lo tanto cumple con tener la menor prioridad del conjunto
X (i, k ) .
b) Si xk es la raíz, entonces xi no es un ancestro de xk ; y xi tampoco tiene la menor prioridad de
los elementos de X (i, k ) , ya que en este caso la menor prioridad la tiene xk .
c) Si otro nodo, x j es la raíz; entonces xi y xk están en diferentes subárboles, y por lo tanto uno
no puede ser ancestro del otro. Se cumple además que i < j < k o bien que i > j > k; es decir x j
pertenece al subconjunto X (i, k ) y tiene la prioridad menor del conjunto; por lo que xi
obviamente no tiene la prioridad menor del conjunto.
j j
i
k
i
k

Figura 16.5. Caso c). Nodo j es la raíz.

d) Si xi y xk no son la raíz y están en el mismo subárbol, se desciende hasta que uno de ellos
sea la raíz o hasta encontrar una raíz x j que los deje en subárboles diferentes y se vuelven
(inductivamente) a aplicar las consideraciones realizadas en a), b) y c).

Como cada nodo del subconjunto X (i, k ) tiene una prioridad elegida en forma independiente,
como una variable aleatoria con una distribución continua, que i tenga la prioridad menor del
conjunto ocurre con probabilidad:

1
Pr(ai ,k 1)
|k i| 1
Es decir, es uno entre todos los nodos del conjunto.

El resultado anterior permite calcular el valor esperado para la altura, definida como el número
de nodos del trayecto de xk hasta la raíz, según:

i n i k 1 i n
1 1
h Pr(ai ,k 1)
i 1 i 1 k i 1 i k 1i k 1

Donde la primera sumatoria es para i < k y la segunda para i > k.

Efectuando (k-i+1) = j en la primera sumatoria y (i-k+1) = j en la segunda, se obtiene:

Profesor Leopoldo Silva Bijit 26-05-2008


8 Estructuras de Datos y Algoritmos
j k j n k 1
1 1
h
j 2 j j 2 j

Iniciándolas desde 1, y reconociendo la serie armónica se logra:

j k j n k 1
1 1
h 2 H (k ) H (n k 1) 2
j 1 j j 1 j
Con H (n) la serie armónica:
i n
1
H ( n) ln(n)
i 1 i
Con =0,577. Para n >>1, se tiene:

h ln(n) O(log(n))

Debemos suponer que k >1 y n > k, es decir un treap que contenga un número mayor de nodos
que la k-ésima clave menor del treap. Con estas condiciones puede realizarse, mediante Maple,
las siguientes gráficas, para ilustrar la complejidad de la altura de un treap.

> h:=sum(1/(k-i+1),i=1..k-1)+sum(1/(i-k+1),i=k+1..n);
> k:=30;
> plot([1.1*ln(n)/ln(2),h,0.8*ln(n)/ln(2)],
n=k..100*k,color=[red,black,blue],thickness=2);

Se han elegido constantes de 1,1 y 0,8 para acotar por arriba y por abajo a la función h.
Los diagramas, para la 30ava clave menor del treap, se muestran en la Figura 16.6.

Figura 16.6. Valor esperado para la altura de un nodo con valor pequeño de clave.

Las gráficas muestran que la altura tiene complejidad: h (log( n))

Entonces la complejidad temporal de las operaciones de búsqueda, inserción y descarte en un


treap, tienen una cota esperada que varía logarítmicamente con el número de nodos.

Profesor Leopoldo Silva Bijit 26-05-2008


Árboles de búsqueda aleatorizados. Treaps. 9
16.2.3. Rotaciones.

Puede determinarse el largo del trayecto formado por los descendientes izquierdos de la raíz
(que se define como espina o columna vertebral izquierda). Si suponemos un treap formado por
n claves y consideramos, sin perder generalidad, que las claves almacenadas en el árbol son los
números de 1 a n, podemos definir el indicador X i ,k con valor uno, si el nodo con clave i está en
la espina izquierda del subárbol con raíz k; y cero en caso contrario.

En un treap debe cumplirse que k > i y la prioridad del nodo con clave k debe ser menor que la
prioridad del nodo con clave i. Además para cada nodo con clave z, con i < z < k, debe
cumplirse que la prioridad de z debe ser menor que la prioridad del nodo con clave i.
k

Figura 16.7. Espina izquierda con raíz k.

La probabilidad de que el indicador X i ,k sea uno, puede calcularse según:

(k i 1)! 1
Pr[ X i ,k 1]
(k i 1)! (k i)(k i 1)

El numerador contabiliza el número de permutaciones que pueden escribirse con las claves
desde i hasta k, es decir con (k-i+1) claves. De todas las anteriores, aquellas que comienzan en k
y terminan en i, son las que pueden generarse con las permutaciones de las cifras contenidas
entre ambas; es decir con (k-i-1) claves.

Si para cada permutación se ingresan las claves en el orden de la permutación se producirán


diferentes árboles binarios; el numerador describe las diferentes espinas izquierdas que pueden
formarse; todas aquellas que tengan por raíz k, y cuyo último nodo de la espina sea i.

El valor esperado para el largo de la espina izquierda, es la suma de los nodos que están
presentes en la espina:
i k 1 i k 1
1
E(I ) Pr[ X i ,k 1]
i 1 i 1 (k i)(k i 1)

Efectuando el cambio de variable (k-i) por j en la sumatoria anterior, se obtiene:

j k 1
1 1
E(I ) 1
j 1 j ( j 1) k

Profesor Leopoldo Silva Bijit 26-05-2008


10 Estructuras de Datos y Algoritmos
El resultado de la sumatoria puede demostrarse por inducción.
Se tiene la propiedad:
j k 1
1 1
P(k ) 1
j 1 j ( j 1) k
Se cumple para k=2
1 1
P(2) 1
12 2
Entonces se tiene:
j ( k 1) 1 j k 1
1 1 1 1
P(k 1) P(k )
j 1 j ( j 1) j 1 j ( j 1) k (k 1) k ( k 1)

1 1 1
P(k 1) 1 1
k k (k 1) k 1

Demostrando que la propiedad se cumple para todo k.

El valor esperado del largo de la espina derecha, se realiza de igual forma, pero sumando desde
1 hasta (n-k).
j n k
1 1
E ( D) 1
j 1 j ( j 1) n k 1

Para E(D) puede considerarse E(I) y realizar el cálculo por simetría.

Si xk es la raíz, la espina izquierda se calcula sumando Pr[ X i ,k 1] desde i igual a 1 a (k-1); para
la espina derecha pueden cambiarse los índices para sumar desde i igual 1 hasta (n-k), según se
muestra en la Figura 16.8. A la derecha se ilustra con i=5, k=8 y n=11.
k 8

k-1 k+1 n-k 7 9 3


6 10
i n 1 5 11 1

Figura 16.8. Simetría en el cálculo de espina derecha, a partir de la espina izquierda.

El número de rotaciones necesarias para insertar una determinada clave es el mismo que el
número de rotaciones para descartarla. Para descartar un clave se realizan rotaciones que la
hacen descender; en cada rotación a la izquierda la espina derecha disminuye en uno, y en cada
rotación a la derecha la espina izquierda disminuye en uno. Al llegar la clave a la posición de
una hoja, ambas espinas son cero. Entonces el número de rotaciones para descartar una clave es
la suma de las espinas derecha e izquierda de esa clave, antes de descartarla.

Para la clave k, se tiene:

Profesor Leopoldo Silva Bijit 26-05-2008


Árboles de búsqueda aleatorizados. Treaps. 11
1 1
rotaciones E ( D) E ( I ) 2 2
k n k 1

Expresión que es menor que 2 para: n > k > 0.

Lo que implica que el número de rotaciones en las operaciones de inserción y descarte en


promedio son menores que 2. Es decir, para mantener el balance por medios aleatorios, se
agrega una complejidad constante a las funciones de inserción y descarte.

Los siguientes comandos Maple, muestran las gráficas del número de rotaciones para tres
valores de k.
> rot:=2-1/k-1/(n-k+1);
> subs(k=1,rot),subs(k=n/2,rot),subs(k=n,rot);
1 2 1 1
1 ,2 ,1
n n 1 n
n 1
2
> plot([subs(k=1,rot),subs(k=n/2,rot),subs(k=n,rot)],n=1..20,
color=[red,black,blue],thickness=2);

Para k=n/2, el número de rotaciones es menor que 2, lo que se muestra en la Figura 16.9.

Figura 16.9. Número de rotaciones.

16.3. Estructura de datos.

Se emplea un tipo especial para la clave, para facilitar los cambios en caso que la clave no sea
entera.
typedef int data;

typedef struct node {


struct node *left, *right;
unsigned int prioridad;
data clave;
} nodo, *pnodo;

Profesor Leopoldo Silva Bijit 26-05-2008


12 Estructuras de Datos y Algoritmos

typedef pnodo arbol;

Si se emplea un centinela en la base del árbol.


pnodo nil;
nodo centinela;

void initglobalvariables()
{ nil=&centinela;
nil->prioridad = UINT_MAX; //
nil->left = nil; nil->right = nil;
}

16.4. Creación de nodo.

pnodo getnodo(data valor)


{ pnodo p = (pnodo) malloc(sizeof(nodo));
if (p==NULL) {printf("Error memoria\n"); exit(1);}
else
{ p->clave = valor;
p->left = nil; p->right = nil; //apuntan al centinela
p->prioridad =q(valor); //se crea con determinada prioridad. //rand();
}
return(p);
}

Puede no almacenarse la prioridad en el nodo. Cuando las operaciones de inserción y descarte la


requieran se la calcula evaluando con la función de hash: q(p->clave).

Si la clave no es numérica puede tomarse q(&p), para generar la prioridad del nodo.

16.5. Función de hash. Generador aleatorio de prioridades.

Según los autores del algoritmo basta un polinomio de grado 4 en actualizaciones.


Los coeficientes se pueden calcular con el generador aleatorio rand().
#define U 20117
#define c4 397
#define c3 965
#define c2 140
#define c1 730
#define c0 498
unsigned int q(int i)
{ return ( (((c4*i + c3)*i+ c2)*i +c1)*i+c0)%U; }

Si se disminuye U, se tendrán algunas colisiones, pero que no degradan el funcionamiento de las


operaciones. Aumenta levemente el número de rotaciones necesarias para reestablecer el
balance.

Profesor Leopoldo Silva Bijit 26-05-2008


Árboles de búsqueda aleatorizados. Treaps. 13
16.6. Rotaciones

Las rotaciones se implementan como macros. Se agregan globales para contar las rotaciones, en
el momento de la depuración, luego pueden eliminarse los contadores.
int opl=0;
int opr=0;

#define rrotm(t) do { \
{ temp=t; \
t = t->left; \
temp->left = t->right; \
t->right = temp; \
opr++; \
} \
} while(0)

#define lrotm(t) do { \
{ temp=t; \
t = t->right; \
temp->right = t->left;\
t->left= temp; \
opl++; \
} \
} while(0)

16.7. Insertar.

pnodo insertar(data x, pnodo t) //Diseño recursivo.


{ pnodo temp;
if (t == nil) {t=getnodo(x); } //inserta en hojas
else
{
if (x < t->clave)
{t->left=insertar(x, t->left);
//en el ascenso mantiene propiedades de heap
//si son prioridades iguales no rota
if ( t->left->prioridad < t->prioridad) rrotm(t); //t=rrot(t);
}
else if (x> t->clave)
{t->right=insertar(x, t->right);
if (t->right->prioridad < t->prioridad) lrotm(t); //t=lrot(t);
}
//si la clave ya estaba, no se inserta ni revisa propiedades.
}
return (t);
}

Profesor Leopoldo Silva Bijit 26-05-2008


14 Estructuras de Datos y Algoritmos
16.8. Descartar.

Diseño iterativo. Se pasa la raíz del árbol por referencia.


La variable dir indica la vía por la que se desciende, y pp apunta al padre del nodo actual.
Se ha definido como macro las acciones necesarias para ajustar el padre, luego de las rotaciones,
de acuerdo a la dirección de descenso. Estas operaciones podrían agregarse a los macros de las
rotaciones, sin embargo se las mantiene aparte para mantener el concepto puro de rotación.

#define raiz 2
#define izq 1
#define der 0
#define AjustaPadre(p) if (dir==der) pp->right=(p); else if(dir==izq) pp->left=(p);else if(dir==raiz)*t=(p);

void descartar(data x, arbol *t)


{ pnodo p=*t, pp, temp;
int dir=raiz;
if (p!=nil)
{ while(p!=nil && p->clave!=x) //buscar con padre y dirección de descenso.
{ pp=p;
if(p->clave > x) {dir=izq; p=p->left;}
else {dir=der; p=p->right;}
}
if(p!=nil && p->clave==x) //lo encontró
{ //printf(" %d-> %d", pp->clave, p->clave); putchar('\n');
while(p->left!=nil || p->right!=nil) //no haya llegado a una hoja
{ if (p->left->prioridad < p->right->prioridad)
{ rrotm(p); //p=rrot(p);
AjustaPadre(p);
pp=p; //nuevo padre
dir=der; p=p->right; //desciende por la derecha
}
else //especular
{ lrotm(p); //p=lrot(p);
AjustaPadre(p);
pp=p; //nuevo padre
dir=izq; p=p->left; //desciende por la izquierda
}
}
//p es hoja
AjustaPadre(nil);
free(p); //se puede retornar p, y liberar fuera de la operación descarte.
}
//si no lo encontró no hace nada
}
//si árbol vacío no hace nada
}

Profesor Leopoldo Silva Bijit 26-05-2008


Árboles de búsqueda aleatorizados. Treaps. 15
16.9. Operaciones útiles en depuración.

La función espinas cuenta los nodos de las espinas derecha e izquierda.

int espina=0;
int espinas(pnodo t)
{ int n=0;
pnodo p;
if (t!=NULL)
if (t!=nil )
{ p=t->left; //cuenta descendientes de t en espina izquierda
while (p!=nil) { n++; p=p->left;}
p=t->right; //cuenta descendientes de t en espina derecha
while (p!=nil) { n++; p=p->right;}
}
return n;
}

La función check efectúa un recorrido en el árbol, verificando las propiedades del treap en cada
nodo.

void check(pnodo p)
{ int k, tipo;
if (p!= nil)
{ check(p->left);
k=0;
//Hay error si prioridad del padre mayor o igual que la del hijo izq
if (p->left->prioridad < p->prioridad ) {tipo=1; k++;}
if (p->left->prioridad == p->prioridad ) {tipo=2; k++;} //warning
//Hay error si prioridad del padre mayor que la del hijo der
if (p->right->prioridad < p->prioridad ){tipo=3;k++;}
if (p->right->prioridad == p->prioridad ){tipo=4;k++;}//warning

//Se pueden agregar algunos test para verificar la estructura de árbol de búsqueda
//hijo izquierdo debe tener clave menor que su padre
if(p->left !=nil) if (p->left->clave >= p->clave ) {tipo=5; k++;}
//hijo derecho debe tener clave mayor que su padre
if(p->right !=nil) if(p->right->clave <= p->clave ) {tipo=6; k++;}

if(k)
{ if(tipo==2 || tipo==4) printf("Warning=%d %d \n", tipo, p->clave);
else printf("Error=%d %d \n", tipo, p->clave);
// prtprioridades(tree); putchar('\n');
}
check(p->right);
}
}

Profesor Leopoldo Silva Bijit 26-05-2008


16 Estructuras de Datos y Algoritmos

void prtprioridades(pnodo p) //Imprime en orden las claves y sus prioridades.


{ if (p!= nil)
{ prtnivel(p->left);
printf ("%d,%u ", p->clave, p->prioridad);
prtnivel(p->right);
}
}

16.10. Test de las funciones.

#define N …
int instrucciones=0;
int main(void)
{ int i;
clock_t start, stop; //tipo definido en time.h
int totaltime = 0;
initglobalvariables();
tree=nil;
start = clock();

srand(1);
for(i=1; i<=N; i++)
{ int aleatorio=rand()%1023;
tree=insertar(aleatorio, tree);
check(tree);
espina+=espinas(Buscar(aleatorio, tree));
//prtinorder(tree); putchar('\n');
}
instrucciones+=N;

for(i=1; i<=N; i++)


{ espina+=espinas(Buscar(tree->clave, tree));
descartar(tree->clave, &tree);
check(tree);
//prtinorder(tree);putchar('\n');
}
instrucciones+=N;
stop = clock();
totaltime += (stop-start); // Mantiene el tiempo usado por la acción. Aproximadamente.
printf("Tiempo acumulado = %d [ticks]\n", totaltime);
printf("\nOpl=%d Opr=%d Op=%d\n",opl, opr, opl+opr);
printf("\nEspinas= %d \n", espina);
printf("Rotaciones promedio= %f\n ", (float)(opl+opr) /(float)instrucciones );
printf("\nFin test \n");
return (0);
}

Profesor Leopoldo Silva Bijit 26-05-2008


Árboles de búsqueda aleatorizados. Treaps. 17
Ejercicios propuestos.

E16.1.

A veces es deseable partir un conjunto de items X en dos conjuntos. Uno X1 con las claves de X
que son menores que a; y otro X2 con las claves mayores que a. O bien es necesario unir dos
conjuntos X1 y X2, cuando se asume que las claves del conjunto X1 son menores que las claves
del conjunto X2.

Estas operaciones son fáciles de implementar con las operaciones de inserción y descarte en un
treap. Para partir (split) un treap que almacena X, basta insertar la clave a, con prioridad
mínima, lo cual lleva dicho ítem a la raíz; dejando X1 como el subárbol izquierdo y X2 como el
subárbol derecho. Para unir o mezclar (join) se forma un treap con una raíz con prioridad
máxima, y con subárbol izquierdo el treap formado por X1, y con subárbol derecho el treap
formado por X2; luego se descarta la raíz.

Diseñar las funciones:


void split(arbol x, data a, pnodo * x1, pnodo *x2); Que escribe por referencia los árboles X1 y
X2.

pnodo join(pnodo x1, pnodo x2); que retorna x.

E16.2.

Modificar la función de inserción cuando se pasa la dirección del nodo antecesor o sucesor de la
clave que será insertada.
Modificar la función de descarte cuando se pasa la dirección del nodo será descartado.

E16.3.

Describir un método de ordenamiento de la claves de un conjunto X, empleando un treap y sus


funciones. Evaluar el costo.

E16.4.

Modificar la estructura de datos, suprimiendo el campo prioridad. Rediseñar las operaciones de


inserción y descarte en estas condiciones. Evaluar la necesidad de un nodo centinela y si éste
simplifica o no la codificación. Para los descendientes de las hojas puede asumir que su
prioridad es UINT_MAX.

Referencias.

Raimund Seidel, Cecilia Aragon. “Randomized search trees”. Algorithmica 16:464-497, 1996.

Profesor Leopoldo Silva Bijit 26-05-2008


18 Estructuras de Datos y Algoritmos
Índice general.

CAPÍTULO 16. ...........................................................................................................................................1


ÁRBOLES DE BÚSQUEDA ALEATORIZADOS. TREAPS. ...............................................................1
16.1. ANÁLISIS DE LAS OPERACIONES. ......................................................................................................2
16.2. COMPLEJIDAD. .................................................................................................................................5
16.2.1. Conceptos básicos de probabilidad. ........................................................................................5
16.2.2. Altura. ......................................................................................................................................6
16.2.3. Rotaciones. ...............................................................................................................................9
16.3. ESTRUCTURA DE DATOS. ................................................................................................................11
16.4. CREACIÓN DE NODO. ......................................................................................................................12
16.5. FUNCIÓN DE HASH. GENERADOR ALEATORIO DE PRIORIDADES. .....................................................12
16.6. ROTACIONES ..................................................................................................................................13
16.7. INSERTAR. ......................................................................................................................................13
16.8. DESCARTAR. ..................................................................................................................................14
16.9. OPERACIONES ÚTILES EN DEPURACIÓN. .........................................................................................15
16.10. TEST DE LAS FUNCIONES. .............................................................................................................16
EJERCICIOS PROPUESTOS. ........................................................................................................................17
E16.1. .................................................................................................................................................17
E16.2. .................................................................................................................................................17
E16.3. .................................................................................................................................................17
E16.4. .................................................................................................................................................17
REFERENCIAS. .........................................................................................................................................17
ÍNDICE GENERAL. ....................................................................................................................................18
ÍNDICE DE FIGURAS. ................................................................................................................................19

Profesor Leopoldo Silva Bijit 26-05-2008


Árboles de búsqueda aleatorizados. Treaps. 19
Índice de figuras.

FIGURA 16.1. TREAP. .................................................................................................................................... 2


FIGURA 16.2. INSERCIÓN DE CLAVE 8 EN TREAP. .......................................................................................... 3
FIGURA 16.3. INSERCIÓN DE CLAVE 8 EN TREAP. .......................................................................................... 3
FIGURA 16.4. SUBCONJUNTO DE NODOS EN UN TREAP. ................................................................................. 6
FIGURA 16.5. CASO C). NODO J ES LA RAÍZ. .................................................................................................. 7
FIGURA 16.6. VALOR ESPERADO PARA LA ALTURA DE UN NODO CON VALOR PEQUEÑO DE CLAVE. .............. 8
FIGURA 16.7. ESPINA IZQUIERDA CON RAÍZ K. .............................................................................................. 9
FIGURA 16.8. SIMETRÍA EN EL CÁLCULO DE ESPINA DERECHA, A PARTIR DE LA ESPINA IZQUIERDA. ........... 10
FIGURA 16.9. NÚMERO DE ROTACIONES. .................................................................................................... 11

Profesor Leopoldo Silva Bijit 26-05-2008

También podría gustarte