1 Capítulo 12. Árboles coloreados. Red black. Si las claves siempre ingresan ordenadas en forma aleatoria, puede emplearse un árbol binario de búsqueda. Sin embargo si por momentos se producen ingresos de claves en orden, debe escogerse una estructura que pueda rebalancear dinámicamente el árbol. El árbol coloreado, como se verá, tiene un criterio de balance un tanto más relajado que un AVL. Por esta razón, si los datos ingresan preponderantemente ordenados el árbol AVL tiene un mejor comportamiento que el red-black, ya que tiene una altura menor. Si los datos son accesados mayormente en forma secuencial, el árbol desplegado, splay, tiene un mejor comportamiento que los anteriores. 12.1. Propiedades de los árboles coloreados. Se agrega a cada nodo un dato que indica su color. Existen reglas que deben cumplirse para asignar el color a cada nodo, de tal modo que una trayectoria cualquiera desde un nodo a una hoja no sea mayor que el doble del largo de cualquier otra. Esto asegura que el árbol coloreado se mantenga más o menos balanceado, asegurando un costo logarítmico para las operaciones en peor caso. Propiedades: 1. Cualquier nodo es rojo o negro. 2. Cualquier descendiente de hoja se considera negro. (Los nodos externos, son negros; éstos no son nodos con información.) La raíz debe ser negra. 3. Si un nodo es rojo, sus hijos son negros. No hay dos rojos adyacentes. 4. Cualquier trayecto desde un nodo hacia una hoja contiene el mismo número de nodos negros. La Figura 15.1 muestra los nodos con valores 4, 7, 12 y 20 de color rojo. La estructura cumple las propiedades anteriores. 9 15 4 2 5 12 20 7 Figura 12.1 Árbol de búsqueda binaria coloreado. Profesor Leopoldo Silva Bijit 26-05-2008 2 Estructuras de Datos y Algoritmos Una buena interpretación de los nodos rojos en un árbol coloreado, es dibujarlos en un nivel horizontal, de este modo se refleja que no alteran las alturas negras de los nodos. Esto se muestra en la Figura 12.2, para el árbol coloreado de la Figura 12.1. En la representación con rojos en nivel horizontal, debe cuidarse que cada nodo tenga dos hijos, para que el árbol sea binario. También puede verse, con esta representación, que en un árbol coloreado todas las hojas tienen la misma altura. El árbol coloreado es un caso particular de un B-Tree, en los cuales los nodos pueden almacenar varias claves. 4 2 9 5 7 12 15 20 Figura 12.1a Árbol coloreado con rojos horizontales. Debido a la presencia de nodos rojos, al menos la mitad de los nodos de un trayecto de un nodo hasta las hojas deben ser negros. La trayectoria más larga, una que alterna entre nodos rojos y negros, es sólo el doble del largo de la trayectoria más corta, la formada sólo por nodos negros. Figura 12.2 Trayectos en peor caso. Luego de insertar desde el 1 hasta el 14, que es un peor caso de árbol binario de búsqueda, en el árbol coloreado que se muestra en la Figura 12.2, el trayecto más largo es de 6 nodos por la vía más larga y de tres nodos por la más corta. En la Figura 12.2, puede comprobarse que las alturas negras de todos los nodos son iguales, y que no hay dos rojos adyacentes; además la raíz es negra, cumpliendo todas las propiedades. Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 3 12.2. Complejidad en árboles coloreados. En un árbol coloreado, con n nodos internos, debe cumplirse que la altura h es a lo más: h 2log(n 1) Se define la función altura negra de un nodo x, bh(x), como el número de nodos negros de cualquier trayectoria desde x hasta una hoja, no contando el nodo x. Se desea probar, mediante inducción, que un subárbol, que parte en el nodo x, contiene a lo menos 2bh(x)-1 nodos internos. Si x es un nodo externo, entonces bh(x) = 0, y el número de nodos internos es: 20-1=0. Si x es una hoja, entonces bh(x) = 1, y el número de nodos internos es: 21-1=1. Si x tiene alto h, los nodos hijos de x, tienen altura (h-1). Si el hijo es rojo tiene altura negra: bh(x), igual a la del padre Si el hijo es negro tiene altura negra: bh(x)-1, ya que no se cuenta el nodo negro. Considerando verdadera la proposición para el número de nodos internos del subárbol que comienza en x; los subárboles de los hijos de x, deben entonces tener a lo menos: 2bh(x)-1-1 nodos internos. Para obtener el número de nodos internos del subárbol que comienza en x, sumamos los nodos internos de los subárboles hijos, más el nodo interno x, se obtiene: n >= (2bh(x)-1-1) + (2bh(x)-1-1) + 1 = 2(2bh(x)-1) - 1=2bh(x)-1 Lo cual demuestra la proposición inicial. Pero en un árbol coloreado al menos la mitad de los nodos de un trayecto de un nodo hasta las hojas deben ser negros, entonces si h es la altura de un árbol, que comienza en x, se tiene que: bh( x) h 2 Entonces, reemplazando en la expresión de n, en función de bh(x), la cota para bh(x), se obtiene: n 2bh( x) 1 2h / 2 1 Despejando h, se logra: h 2log(n 1) (log(n)) Debe notarse que ésta es la complejidad de peor caso. Al insertar o descartar nodos en un árbol coloreado, pueden violarse las propiedades que los definen; y para mantenerlo coloreado, como se verá más adelante, deben cambiarse los colores Profesor Leopoldo Silva Bijit 26-05-2008 4 Estructuras de Datos y Algoritmos de algunos nodos y también posiblemente efectuar rotaciones. Esto se refleja en un costo adicional para las funciones de inserción y descarte en un árbol binario de búsqueda. Para un árbol AVL, la cota para la altura resulta menor que en árbol coloreado: hAVL 1, 4404 log(n) (log(n)) La Figura 12.2a, muestra la complejidad del árbol coloreado, respecto del AVL, y puede observarse que son muy similares. Pero las operaciones se realizan en menor tiempo, en un árbol coloreado. En un árbol binario de búsqueda, en promedio, si las claves llegan aleatoriamente, se tiene la (log(n)) . Pero ésta se incrementa hasta n, en el peor caso. altura: hBST 1,3863log( n) coloreado AVL Bst promedio balanceado Figura 12.2a. Comparación de complejidades. Red-Black, AVL, Balanceado. 12.3. Análisis de inserción. La inserción de un nuevo nodo siempre se realiza como descendiente de una hoja. Se inserta el nuevo nodo con color rojo, ya que esto no altera las alturas negras de los trayectos. Cuando la hoja donde se insertará el nuevo nodo es roja, se pierde la propiedad de ser un árbol coloreado, ya que se producen dos nodos rojos adyacentes. Si se inserta un nodo con color rojo, y si el árbol estaba vacío, debe cambiarse a negro para mantener la propiedad de que la raíz sea negra. Si se inserta un nodo en la raíz, como ésta es negra, se mantienen las propiedades de los árboles coloreados. En caso de inserción en árboles de mayores niveles, la inserción de un rojo en una hoja roja requiere efectuar modificaciones para preservar las propiedades, en algunos casos bastará una Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 5 recoloración y en otros deben efectuarse rotaciones. A medida que se van efectuando estas modificaciones puede que en el ascenso hacia la raíz se vuelva a producir la situación de dos rojos adyacentes. Estudiaremos los diferentes casos que se producen y las soluciones propuestas para mantener las propiedades. Lo primero que observamos es que cuando se produce un doble rojo, el abuelo del nodo rojo insertado o del rojo que asciende debe ser negro, a este último nodo lo denominaremos con x, en los diagramas. Esto es así ya que antes de la inserción el árbol era coloreado, y por lo tanto no pueden tenerse tres rojos adyacentes. La segunda consideración es que las alturas negras del nodo denominado abuelo deben ser iguales antes y después de la inserción; esto se ilustrará en los diagramas, colocando nodos negros de tal modo de reflejar las alturas negras iguales. Se prefiere esta representación a la de mostrar subárboles descendientes con especificaciones de alturas negras. Una tercera consideración es que el tío del nodo rojo que asciende puede ser negro o rojo, y que deberán analizarse ambos casos. Se ilustran en la Figura 12.3, alturas negras del abuelo iguales a dos (más la altura negra de los descendientes, que no se muestran), se muestra el tío de color negro. Dicho de otra forma, el diagrama insinúa que las alturas negras del tío y de los hijos negros de los nodos rojos deben ser iguales, y menores en una unidad a la altura negra del abuelo. abuelo abuelo tío tío x x Figura 12.3 Dobles rojos en inserción. Existen dos casos adicionales, que son las imágenes especulares de las mostradas en la Figura 12.3, y que corresponden a los casos en que el nodo x es insertado como descendiente derecho del nodo abuelo. Existen dos situaciones que deben analizarse: una corresponde a tío rojo, la otra a tío negro. 12.3.1. Recoloración. Cuando el tío es rojo. Si en la Figura 12.4 a la izquierda, se cambian los colores del abuelo, el padre y el tío, se mantienen las alturas negras del nodo abuelo hacia abajo, y no se tienen dos rojos adyacentes. Sin embargo es necesario seguir la revisión ascendente debido a que el cambio de color del abuelo podría producir nuevamente un doble rojo en el trayecto hacia la raíz. Es decir debe volver a repetirse el proceso ascendiendo el puntero x a la posición del abuelo. Si los cambios se propagarán hasta el caso en que el abuelo es la raíz, debería cambiarse el color de ésta, pero se mantendrían altura negras iguales; en este caso aumenta la altura negra del árbol. Profesor Leopoldo Silva Bijit 26-05-2008 6 Estructuras de Datos y Algoritmos abuelo padre abuelo padre tío x tío x Figura 12.4 Recoloración en inserción. Tío rojo. Cuando el nodo denominado x es descendiente derecho del padre, se soluciona de igual forma. 12.3.2. Rotaciones. Cuando el tío es negro. Se ilustra el caso en que el padre y el nodo x son descendientes izquierdos del nodo abuelo. Si se cambian colores al padre y al abuelo, se disuelve el doble rojo. Sin embargo se altera la altura negra del subárbol derecho del abuelo, como se muestra en la Figura 12.5, disminuye su cuenta en uno. abuelo padre abuelo tío x padre tío x Figura 12.5 Recoloración con tío negro, no quedan dos rojos adyacentes. Si se efectúa una rotación a la derecha de la pareja padre-abuelo, en la Figura 12.5 a la derecha se tiene la Figura 12.6 derecha. A partir del nodo padre hacia abajo se mantienen los largos negros de los subárboles y además no se puede volver a producir un doble rojo en el trayecto de ascenso hacia la raíz, ya que el nodo, denominado padre, en la Figura 12.6 derecha, es negro. Nótese que en la Figura 12.6 a la derecha, se han conservado los nombres originales para los nodos: padre, abuelo y tío; sin embargo luego de la rotación éstos nombres pierden su significado original. También puede observarse que luego de la rotación el sub-árbol, cuya raíz es el padre, queda más balanceado. Esta situación da por terminada la revisión ascendente. abuelo padre tío padre abuelo x x tío Figura 12.6 Rotación derecha, preserva altura negras. Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 7 Aún resta analizar el caso en que el doble rojo se produce como hijo derecho de un padre rojo descendiente de un abuelo negro (y con tío negro). Lo cual se ilustra en la Figura 12.7. abuelo padre abuelo tío x tío padre x Figura 12.7. Rotación izquierda, par padre-x. Antes de producirse este doble rojo, ya sea por inserción o por revisión ascendente, las alturas negras del padre (del abuelo y sus ancestros) eran iguales. La rotación izquierda del par xpadre, respecto al padre, no altera esas cuentas. La Figura 12.7 a la derecha, es el caso analizado antes, que se muestra en la Figura 12.5; para el cual ya se obtuvo una solución, pero cambiando el puntero x a la posición ocupada finalmente por el padre. Los casos en que el nodo x es agregado como descendiente del subárbol derecho del abuelo son situaciones con simetría especular a los tratados. Un caso particular es cuando el tío no existe, es decir cuando es un nodo externo; en este caso se considera tío de color negro. Esto completa el análisis de la inserción en un árbol coloreado. 12.4. Análisis de la operación descartar. Deben analizarse tres casos, que el nodo que se desea descartar tenga dos descendientes, uno solo o ninguno. Cuando se borra un nodo rojo se mantienen las propiedades de los árboles coloreados. Ya que no cambian las alturas negras de los nodos. 12.4.1. Dos descendientes. Cuando se borra un nodo apuntado por t, que tiene los dos subárboles descendientes, se elige el mayor descendiente del subárbol izquierdo (I) o el menor descendiente del subárbol derecho (D) y se cambian los datos del nodo que será eliminado con el seleccionado. Si el nodo seleccionado (I o D) es rojo, no puede tener descendientes y será una hoja; estos casos se muestran en la Figura 12.8. Profesor Leopoldo Silva Bijit 26-05-2008 8 Estructuras de Datos y Algoritmos t t I D D I Figura 12.8. Nodo seleccionado para ser descartado es hoja roja. Si el nodo seleccionado es negro, sólo puede tener un hijo rojo ya que el árbol es coloreado, antes del descarte. Situación que se muestra en la Figura 12.9. t t I D I D Figura 12.9. Nodo seleccionado para ser descartado es nodo negro con hijo rojo. Luego de copiar los datos del nodo seleccionado en el nodo apuntado por t, se procede a descartar el seleccionado; el cual debe ser una hoja roja o un nodo negro con sólo un hijo que debe ser rojo. Estos casos se analizan a continuación. 12.4.2. Un descendiente. Cuando se descarta un nodo t con un solo hijo, se presentan tres casos, que se ilustran en la Figura 12.10, cuando t tiene hijo izquierdo y es un descendiente izquierdo. Debe notarse que el nodo t debe ser negro, y que su único hijo debe ser rojo. t t t Figura 12.10 Descartar nodo negro con un hijo rojo. Se preservan las propiedades de los árboles coloreados si se liga el hijo de t, con el padre de éste; y se le cambia el color a negro, para mantener iguales alturas negras, y no tener dos rojos adyacentes. Igual solución se aplica si el hijo de t es descendiente derecho. Las soluciones se muestran en la Figura 12.10a. t t t Figura 12.10a Descartar nodo negro con un hijo rojo. Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 9 Si t es descendiente derecho, la solución es la especular de la analizada. 12.4.3. El nodo a descartar es una hoja. Sin descendientes. En este caso se presentan cinco situaciones, que se ilustran en la Figura 12.11. t t t t t Figura 12.11 Descartar hoja. Cuando el padre es rojo, éste debe tener dos hijos negros. Luego de borrar el nodo hay que cambiar el color del padre y del tío; esto preserva iguales alturas negras. Cuando el nodo para descartar es rojo, hay dos casos: con hermano nulo o con hermano rojo. Ambos casos no requieren modificaciones, y sólo es preciso borrar el nodo. Cuando el nodo y su padre son negros se tienen dos casos, con hermano negro o rojo. En este último caso, luego de borrar el nodo el árbol deja de ser coloreado, ya que disminuye la altura negra del subárbol izquierdo; se soluciona rotando a la izquierda, el par padre-hermano, y cambiando de rojo a negro el color del nuevo padre, para igualar las alturas negras. Lo cual se muestra en la Figura 12.12. En este caso, como el hermano es rojo, el padre necesariamente debe ser negro. t Figura 12.12 Descartar hoja negra con hermano rojo. El último caso que debe analizarse es el de hijo, padre y hermano de colores negros. Si se elimina la hoja y se cambia a color rojo el huérfano, se produce una disminución de la altura negra del nodo padre, denominado x, en la Figura 12.13, lo cual hará perder al árbol sus propiedades. Nótese que de x hacia abajo el árbol es coloreado. x t Figura 12.13 Descartar hoja negra con padre y hermano negro. La altura negra de x es bh(x)=0, si no se cuenta el nodo externo; y uno si se cuenta el nodo externo. Profesor Leopoldo Silva Bijit 26-05-2008 10 Estructuras de Datos y Algoritmos Para corregir el balance de alturas negras debe efectuarse una revisión ascendente. 12.4.4. Balance de alturas negras en descarte. Caso doble negro. Sea x un puntero al subárbol coloreado, que disminuyó su altura negra, y desde el cual debe revisarse hacia arriba para mantener la propiedad de iguales alturas negras de los subárboles. La raíz de este subárbol es negra. Si x es la raíz debe detenerse la revisión. a) Hermano rojo. Si x tiene padre y el hermano h es rojo, el padre debe ser negro. La Figura 12.14, a la izquierda, ilustra este caso, cuando x es descendiente izquierdo. x h h x h x h Figura 12.14 Hermano rojo, padre negro. Si la altura negra de x es bh(x), las alturas negras de los subárboles descendientes del hermano rojo son iguales a bh(x)+1. Esto se ilustra con dos descendientes negros en los subárboles de h. Si el árbol era coloreado, antes del descarte, necesariamente x tiene padre, hermano y descendientes. Si se cambia el color del padre y del hermano se tiene el resultado que se muestra en la Figura 12.14, al centro. Luego rotando a la izquierda el par padre-hermano, se obtiene la situación mostrada a la derecha de la Figura 12.14, en la cual se ha reposicionado h, apuntado al hermano actual de x. Tanto el actual abuelo como el padre de x, siguen desbalanceados. El objetivo de esta transformación es lograr que x tenga un hermano negro. Que es el siguiente caso que se estudia. b) Hermano negro. Si el hermano es negro, el padre puede ser rojo o negro. Lo cual se representará por un signo de interrogación al lado del nodo. En esta situación se tienen tres casos que analizar: Una es si ambos hijos del hermano son negros; la segunda es si el hijo derecho del hermano es negro y el izquierdo es rojo; y la tercera es que el hijo derecho del hermano sea rojo, pudiendo ser rojo o negro el hijo izquierdo del hermano. b1) Sobrinos negros. Si x y su hermano son negros, los subárboles descendientes de h, tienen altura negra igual a bh(x)+1, lo cual se insinúa con un descendiente, en la Figura 12.15. Esto es así, ya que originalmente se borró un descendiente negro de x, cuando el árbol era coloreado. Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 11 Si se cambia el color del hermano a rojo, y se mueve x, apuntando al padre, se tendrá que desde el nuevo x hacia abajo el árbol es coloreado. Además se fuerza a negro el padre original de x, para evitar un posible doble rojo. ? x x h h x Figura 12.15 Hermano negro y sobrinos negros. Luego de esta transformación la altura negra del nuevo x, aumenta en uno; y se debe seguir revisando en forma ascendente hacia la raíz. No es necesario continuar, si el nuevo x es la raíz. b2) Sobrino derecho negro, sobrino izquierdo rojo. Como x y su hermano son negros, el padre de x puede ser rojo o negro. Nuevamente los descendientes de h, deben tener alturas negras: bh(x)+1. ? h x h x ? h h ? h x h h Figura 12.16 Hermano y sobrino derecho negros. Primero se cambian los colores del hermano negro y del sobrino rojo, esto se muestra en el medio de la Figura 12.16. Luego se rota a la derecha el par hermano-sobrino derecho, lo cual se muestra a la derecha en la Figura 12.16, en la cual se mueve h, para apuntar al nuevo hermano de x. Esta transformación conduce al caso siguiente, en la que el sobrino derecho es rojo. b3) Sobrino derecho rojo, sobrino izquierdo negro. El padre de x, puede ser rojo o negro. Se procede a copiar el color del padre en el hermano, y pintar de negro al padre y al sobrino rojo, resultado que se muestra al centro de la Figura 12.17. x ? h h x ? h ? h h x Figura 12.17 Hermano negro, sobrino derecho rojo. Profesor Leopoldo Silva Bijit 26-05-2008 h 12 Estructuras de Datos y Algoritmos Luego se rota a la izquierda el par padre-hermano, resultando la ilustración a la derecha de la Figura 12.17. En ésta se ha logrado reestablecer las alturas negras de todos los nodos desde h hacia abajo. No es preciso continuar la revisión ascendente, y el árbol completo es coloreado. Si h queda apuntando a la raíz, debe forzarse el color negro en ese nodo. El caso en que ambos sobrinos son rojos, se trata de igual forma que el caso b3). x ? h h Figura 12.18 Hermano negro, sobrinos rojos. Esto completa el análisis del descarte de un nodo. 12.5. Estructura de datos y funciones básicas. 12.5.1. Estructura de datos. Si las operaciones de inserción y descarte se implementan en forma recursiva, la ruta de búsqueda queda almacenada en el stack, es decir se disponen de las referencias al padre y al abuelo. Después de un retorno de una función que tiene como argumento un nodo apuntado por t; se tiene ahora en t, el puntero al padre del anterior. De esta forma es posible definir un nodo con sólo dos punteros, el izquierdo y el derecho, además del color. Sin embargo si las exigencias de espacio no son fundamentales la elección de una estructura que adicionalmente mantenga un puntero al padre del nodo, presenta ventajas en la velocidad de ejecución de las operaciones. Además las funciones resultan iterativas, y no se produce un gran espacio del stack, para almacenar los argumentos de las funciones recursivas. typedef struct nn { /* RB-Tree */ int color; /* Solo puede ser Rojo o Negro */ int clave; /* clave entera */ struct nn *left, *right, *padre; /* tres punteros */ } nodo, *pnodo; 12.5.2. Crea un nodo para inserción. Se inicia el nodo con color rojo. #define RED 0 #define BLACK 1 Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 13 pnodo CreaNodo(int valor) { pnodo t = (pnodo) malloc(sizeof(nodo)); if (t != NULL) { t->color=RED; t->clave=valor; t->left=NULL; t->right=NULL; t->padre=NULL; } return t; } 12.5.3. Sucesor. El diseño del sucesor es simple si se dispone de un puntero al padre. pnodo sucesor(pnodo x) { pnodo y; if (x->right!=NULL) { /* Si hay subárbol derecho desciende por la izquierda en éste, ** hasta encontrar nodo sin descendiente izquierdo. */ for (y=x->right; y->left!=NULL; y=y->left); } else { /* Ascender hasta encontrar nodo que esté a la izquierda de su padre ** ( o la raíz) entonces retornar el padre. */ for(y=x->padre; y!=NULL && x==y->right; x=y, y=y->padre ); } return(y); } 12.5.4. Predecesor. Basta cambiar left por right, y viceversa, en el código del sucesor. pnodo predecesor(pnodo x) { pnodo y; if (x->left!=NULL) for (y=x->left; y->right!=NULL; y=y->right); else { y=x->padre; while(y!=NULL && x==y->left){x=y; y=y->padre;} } return(y); } Profesor Leopoldo Silva Bijit 26-05-2008 14 Estructuras de Datos y Algoritmos 12.5.5. Rotación a la izquierda. Se pasa la raíz del árbol por referencia. Esto en caso que el nodo Y, se convierta en la nueva raíz. /* ** Rotación izquierda con padre ** ** X lrot(X) ---> Y ** / \ / \ ** A Y <--rrot(Y) X C ** / \ / \ ** B C A B ** Se asume que x o y no son nulos */ void lrot(pnodo * raiz, pnodo x) { pnodo y; //assert(x!=NULL); //assert(x->right!=NULL); y=x->right; /* y es el hijo derecho de x */ /* Pega subárbol izquierdo de y, como subárbol derecho de x.*/ x->right = y->left; /* Si B no es nulo, el padre de B ahora es x */ if (y->left != NULL) y->left->padre = x; /* Padre de y es el padre de x */ y->padre = x->padre; if (x->padre == NULL) *raiz=y; /* Si x es la raíz, la deja apuntando a y */ /* Si x era descendiente izquierdo, pega y por la izquierda */ else if (x == x->padre->left) {x->padre->left=y;} else {x->padre->right=y;} //sino por la derecha y->left=x; /* Al nodo y le pega x */ x->padre = y; /* Le pega el padre a x */ } Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 15 12.5.6. Rotación a la izquierda. Es el código especular del anterior. /* ** Rotación derecha con padre ** ** X rrot(X) ** / \ ** Y C <--** / \ ** A B ** ** ** Se asume que x o y no son nulos */ ---> lrot(Y) Y / \ A X / \ B C void rrot(pnodo * raiz, pnodo x) { pnodo y; //assert(x!=NULL); //assert(x->left!=NULL); y=x->left; /* y es el hijo izquierdo de x */ /* Pega subárbol derecho de y, como subárbol izquierdo de x.*/ x->left = y->right; /* Si B no es nulo, el padre de B ahora es x */ if (y->right != NULL) y->right->padre = x; /* Padre de y es el padre de x */ y->padre = x->padre; if (x->padre == NULL) *raiz=y;/* Si x es la raíz, la deja apuntando a y */ /* Si x era descendiente derecho, pega y por la derecha */ else if (x == x->padre->right) {x->padre->right=y;} else {x->padre->left=y;} y->right=x; /* Al nodo y le pega x */ x->padre = y; /* Le pega el padre a x */ } 12.5.7. Comenta errores en inserción y descarte. void Error(int tipo, int clave) { if (tipo==1) printf("Error en inserción. No se pudo crear Nodo\n"); else if (tipo==2) Profesor Leopoldo Silva Bijit 26-05-2008 16 Estructuras de Datos y Algoritmos printf("Error en inserción. Clave duplicada=%d!\n", clave); else if (tipo==3) printf("Error en descarte. Clave no encontrada!\n"); else printf("Error en tipo=%d!\n", tipo); } 12.5.8. Calcula altura negra. //revisa que se cumpla largos de negros de subárboles iguales, de cada nodo. //Retorna -1 si hay error; sino entrega la altura negra, la función black height, bh(x). int bh(pnodo x) { int nleft, nright; if (x==NULL) return(1); //cuenta el descendiente de hoja como negro. nleft=bh(x->left); nright=bh(x->right); //si hay error al retornar de los llamados recursivos if (nleft==-1 || nright==-1) return(-1); //propaga el error if (nleft != nright) { printf("Negros del izquierdo=%d difieren de los del derecho=%d, clave=%d\n", nleft, nright, x->clave); return(-1); } if (x->color == BLACK) { nleft++; //acumula los negros en nleft } return(nleft); } 12.5.9. Revisa propiedades de árbol coloreado. int RevisaPropiedades(pnodo x) { int bhl=1,bhr=1; if (x==NULL) {printf("Árbol vacío\n"); return (1);} if (x->left==NULL && x->right==NULL) { if(x->color!=BLACK) printf("Raíz no es negra\n"); return(1); } if (x->left!=NULL && x->right==NULL) if(x->color!=BLACK && x->left->color!=RED) { printf("Nodo con solo hijo izquierdo no es negro ni tiene hijo rojo\n"); return(1); Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 17 } if (x->right!=NULL && x->left==NULL) if(x->color!=BLACK && x->right->color!=RED) { printf("Nodo con solo hijo derecho no es negro ni tiene hijo rojo\n"); return(1); } if (x->color==RED) { //revisar if (x->left->color!=BLACK && x->right->color!=BLACK) { printf("Rojo con dos hijos que no son negros, x=%d\n", x->clave); return(1); } } if (x->left != NULL) { if (x->left->padre != x) { printf("Hijo izquierdo de x no apunta al padre, x=%d", x->clave); return(1); } bhl=bh(x->left); if (bhl==-1) { printf("Subárbol izquierdo no es coloreado\n"); return(1); } } if (x->right != NULL) { if (x->right->padre != x) { printf("Hijo derecho de x no apunta al padre, x=%d", x->clave); return(1); } bhr=bh(x->right); if (bhr==-1) { printf("Subárbol derecho no es coloreado\n"); return(1); } } if(bhl!=bhr) { printf("Arbol no es coloreado. l=%d r=%d\n",bhl,bhr); return(1); } return(0); //si cumple propiedades. } Profesor Leopoldo Silva Bijit 26-05-2008 18 Estructuras de Datos y Algoritmos 12.6. Inserción. La codificación resulta sencilla si se ha efectuado previamente el análisis detallado de los diferentes casos, desarrollado en 12.3. Se pasa por referencia el árbol. pnodo insertar(pnodo *tree, pnodo nuevo) { pnodo y=NULL; pnodo x=*tree; if (nuevo==NULL) {Error(1,0); return (NULL);} //Busca posición para insertar. while(x!=NULL) { y = x; if (nuevo->clave < x->clave) x=x->left; else if (nuevo->clave > x->clave) x=x->right; else {Error(2, nuevo->clave); return(NULL);} } //y apunta al padre del lugar de inserción. //malloc no pudo crear nodo //clave duplicada nuevo->padre=y; if (y == NULL) {*tree=nuevo; nuevo->color=BLACK; return(nuevo);} else if (nuevo->clave < y->clave) y->left=nuevo; else y->right=nuevo; /*Código adicional para preservar las propiedades de árbol coloreado*/ x=nuevo; while ( (x->padre!=NULL) && ( x->padre->color==RED) ) //doble rojo { if ( x->padre==x->padre->padre->left) //como la raíz es negra, existe el abuelo de x //si x es descendiente izquierdo del abuelo. { y=x->padre->padre->right; // y apunta al tío if ((y!=NULL)&&(y->color==RED)) // solo recoloración. { x->padre->color=BLACK; y->color=BLACK; //pinta NEGRO al tío y al padre x->padre->padre->color=RED; //el abuelo que era negro, cambia de color x=x->padre->padre; //debe seguir revisando ascendentemente } else //Debe reestructurarse mediante rotaciones. Si y es nulo o y apunta a negro. { if (x==x->padre->right) { x= x->padre; lrot(tree,x);} x->padre->color=BLACK; x->padre->padre->color=RED; rrot(tree, x->padre->padre); } Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 19 } else //Código especular. Si x es descendiente derecho del abuelo. { y=x->padre->padre->left; // y apunta al tío if ((y!=NULL)&& (y->color==RED)) // solo recoloración. { x->padre->color=BLACK; y->color=BLACK; //pinta al tío x->padre->padre->color=RED; //el abuelo que era negro, cambia de color x=x->padre->padre; //debe seguir revisando } else //Debe reestructurarse mediante rotaciones { if (x==x->padre->left) { x= x->padre; rrot(tree,x);} x->padre->color=BLACK; x->padre->padre->color=RED; lrot(tree, x->padre->padre); } } } (*tree)->color=BLACK; //pinta la raíz negra return (nuevo); } 12.7. Descarte. Para marcar con x la raíz de un subárbol, que disminuyó su altura negra, se emplea un nodo externo, de color negro, para almacenar un puntero al padre de éste, cuando el nodo que debe ser descartado es negro y es una hoja. Se pasa como argumento, a la función que restaura las propiedades de un árbol coloreado, en caso de descartar un nodo negro, si el nodo x es un centinela (nodo externo); de esta forma antes de modificar el puntero x, se podrá escribir el valor nulo en el padre de x. /* Descarta nodo z, liberando el espacio*/ //z no debe ser NULL void descarta(pnodo *rootp, pnodo z) { nodo externo; int centinela=0; pnodo x, y; if(z==NULL) Error(3,0); if (z->left == NULL || z->right == NULL) y=z; //un hijo u hoja else y=sucesor(z); //Si hay hijo rojo, lo pega; si es hoja: y->right es nulo, y también x. if (y->left != NULL) x=y->left; else x=y->right; if(y->color==BLACK) { //y es negro. if (x==NULL) {centinela=1; x=&externo; x->color=BLACK;} Profesor Leopoldo Silva Bijit 26-05-2008 20 Estructuras de Datos y Algoritmos //y es hoja; x es nodo externo. x->padre = y->padre; //si y tiene hijo rojo, o si y es hoja } if (y->padre == NULL) { if(centinela) *rootp=NULL; else *rootp=x;} else if (y==y->padre->left) y->padre->left = x; else y->padre->right = x; if (y!=z) { z->clave =y->clave; } if (y->color == BLACK) delete_fix(rootp, x, centinela); free(y); } /* Reestablece propiedades árbol coloreado luego de un descarte */ void delete_fix(pnodo *rootp, pnodo x, int esexterno) { pnodo w; while (x!=*rootp && x->color==BLACK) { if (x==x->padre->left) //x es descendiente izquierdo { w=x->padre->right; //w es el hermano if (w->color==RED) { w->color=BLACK; x->padre->color=RED; lrot(rootp, x->padre); w=x->padre->right; } //ahora el hermano es negro. if ( (w->left==NULL || w->left->color==BLACK) //nodo externo o interno && (w->right==NULL || w->right->color==BLACK) ) { w->color=RED;//ambos sobrinos negros if(esexterno) {x->padre->left=NULL; esexterno=0;} x=x->padre; //cambia x. Asciende un nivel y sigue balanceando. } else { //uno o ambos sobrinos son rojos if (w->right==NULL || w->right->color == BLACK) //externo o interno Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 21 { //sobrino derecho negro w->left->color=BLACK; w->color=RED; rrot(rootp, w); w=x->padre->right; } //ahora el sobrino derecho es rojo w->color=x->padre->color; x->padre->color = BLACK; w->right->color = BLACK; lrot(rootp, x->padre); if(esexterno) {x->padre->left=NULL; esexterno=0;} x=*rootp; //lleva x a la raiz, se sale del lazo } } else //código especular del if { //x es descendiente derecho. w=x->padre->left; if (w->color==RED) { w->color=BLACK; x->padre->color=RED; rrot(rootp, x->padre); w=x->padre->left; } if ( (w->right==NULL || w->right->color==BLACK) && (w->left==NULL || w->left->color==BLACK)) { w->color=RED; if(esexterno) {x->padre->right=NULL; esexterno=0;} x=x->padre; } else { if (w->left==NULL || w->left->color == BLACK) { w->right->color=BLACK; w->color=RED; lrot(rootp, w); w=x->padre->left; } w->color=x->padre->color; x->padre->color = BLACK; Profesor Leopoldo Silva Bijit 26-05-2008 22 Estructuras de Datos y Algoritmos w->left->color = BLACK; rrot(rootp, x->padre); if(esexterno) {x->padre->right=NULL; esexterno=0;} x=*rootp; } } } //Al salir del while: x es la raíz o es rojo. Si es la raíz la pinta negra. x->color=BLACK; //corrige caso con un solo hijo rojo } 12.8. Test de las operaciones. La inserción ascendente, genera el árbol coloreado, que se muestra en la Figura 12.2. #define N 14 pnodo arbol=NULL; int main(void) { int i; printf("Insertando ascendente\n"); for(i=1;i<=N;i++) {insertar(&arbol, CreaNodo(i)); printf("%d->", i); //prtinorder(arbol,0); putchar('\n'); RevisaPropiedades(arbol);} printf("\nDescartando\n"); for(i=1;i<=N;i++) {descarta(&arbol, buscar(&arbol,i )); printf("%d",i); RevisaPropiedades(arbol); } printf("Insertando descendente\n"); for(i=N;i>0;i--) {insertar(&arbol, CreaNodo(i)); printf("%d->",i); //prtinorder(arbol,0); putchar('\n'); RevisaPropiedades(arbol); } printf("\nDescartando\n"); for(i=N;i>0;i--) {descarta(&arbol, buscar(&arbol,i )); Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 23 printf("%d",i); RevisaPropiedades(arbol); } return(0); } Profesor Leopoldo Silva Bijit 26-05-2008 24 Estructuras de Datos y Algoritmos Referencias. R. Bayer. “Symmetric binary B-trees: Data structure and maintenance algorithms.” Acta Informatica 1:290-306, 1972. Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. “Introduction to Algorithms”, Second Edition. MIT Press and McGraw-Hill, 2001. Profesor Leopoldo Silva Bijit 26-05-2008 Árboles coloreados 25 Índice general. CAPÍTULO 12. .......................................................................................................................................... 1 ÁRBOLES COLOREADOS. RED BLACK. ........................................................................................... 1 12.1. PROPIEDADES DE LOS ÁRBOLES COLOREADOS. ................................................................................ 1 12.2. COMPLEJIDAD EN ÁRBOLES COLOREADOS. ...................................................................................... 3 12.3. ANÁLISIS DE INSERCIÓN. ................................................................................................................. 4 12.3.1. Recoloración. Cuando el tío es rojo. ....................................................................................... 5 12.3.2. Rotaciones. Cuando el tío es negro. ........................................................................................ 6 12.4. ANÁLISIS DE LA OPERACIÓN DESCARTAR......................................................................................... 7 12.4.1. Dos descendientes. .................................................................................................................. 7 12.4.2. Un descendiente. ..................................................................................................................... 8 12.4.3. El nodo a descartar es una hoja. Sin descendientes. ............................................................... 9 12.4.4. Balance de alturas negras en descarte. Caso doble negro. ................................................... 10 a) Hermano rojo. ............................................................................................................................................ 10 b) Hermano negro. ......................................................................................................................................... 10 b1) Sobrinos negros. ................................................................................................................................. 10 b2) Sobrino derecho negro, sobrino izquierdo rojo. .................................................................................. 11 b3) Sobrino derecho rojo, sobrino izquierdo negro. .................................................................................. 11 12.5. ESTRUCTURA DE DATOS Y FUNCIONES BÁSICAS. ............................................................................ 12 12.5.1. Estructura de datos. ............................................................................................................. 12 12.5.2. Crea un nodo para inserción. ................................................................................................ 12 12.5.3. Sucesor. ................................................................................................................................. 13 12.5.4. Predecesor. ............................................................................................................................ 13 12.5.5. Rotación a la izquierda. ........................................................................................................ 14 12.5.6. Rotación a la izquierda. ........................................................................................................ 15 12.5.7. Comenta errores en inserción y descarte. ............................................................................. 15 12.5.8. Calcula altura negra. ............................................................................................................ 16 12.5.9. Revisa propiedades de árbol coloreado. ............................................................................... 16 12.6. INSERCIÓN. .................................................................................................................................... 18 12.7. DESCARTE. .................................................................................................................................... 19 12.8. TEST DE LAS OPERACIONES. ........................................................................................................... 22 REFERENCIAS. ........................................................................................................................................ 24 ÍNDICE GENERAL. ................................................................................................................................... 25 ÍNDICE DE FIGURAS................................................................................................................................. 26 Profesor Leopoldo Silva Bijit 26-05-2008 26 Estructuras de Datos y Algoritmos Índice de figuras. FIGURA 12.1 ÁRBOL DE BÚSQUEDA BINARIA COLOREADO. ...........................................................................1 FIGURA 12.1A ÁRBOL COLOREADO CON ROJOS HORIZONTALES. ...................................................................2 FIGURA 12.2 TRAYECTOS EN PEOR CASO. .....................................................................................................2 FIGURA 12.2A. COMPARACIÓN DE COMPLEJIDADES. RED-BLACK, AVL, BALANCEADO. .............................4 FIGURA 12.3 DOBLES ROJOS EN INSERCIÓN. .................................................................................................5 FIGURA 12.4 RECOLORACIÓN EN INSERCIÓN. TÍO ROJO. ...............................................................................6 FIGURA 12.5 RECOLORACIÓN CON TÍO NEGRO, NO QUEDAN DOS ROJOS ADYACENTES..................................6 FIGURA 12.6 ROTACIÓN DERECHA, PRESERVA ALTURA NEGRAS...................................................................6 FIGURA 12.7. ROTACIÓN IZQUIERDA, PAR PADRE-X. .....................................................................................7 FIGURA 12.8. NODO SELECCIONADO PARA SER DESCARTADO ES HOJA ROJA. ...............................................8 FIGURA 12.9. NODO SELECCIONADO PARA SER DESCARTADO ES NODO NEGRO CON HIJO ROJO. ....................8 FIGURA 12.10 DESCARTAR NODO NEGRO CON UN HIJO ROJO. .......................................................................8 FIGURA 12.10A DESCARTAR NODO NEGRO CON UN HIJO ROJO. .....................................................................8 FIGURA 12.11 DESCARTAR HOJA. .................................................................................................................9 FIGURA 12.12 DESCARTAR HOJA NEGRA CON HERMANO ROJO. ....................................................................9 FIGURA 12.13 DESCARTAR HOJA NEGRA CON PADRE Y HERMANO NEGRO. ...................................................9 FIGURA 12.14 HERMANO ROJO, PADRE NEGRO. ..........................................................................................10 FIGURA 12.15 HERMANO NEGRO Y SOBRINOS NEGROS. ..............................................................................11 FIGURA 12.16 HERMANO Y SOBRINO DERECHO NEGROS. ............................................................................11 FIGURA 12.17 HERMANO NEGRO, SOBRINO DERECHO ROJO. .......................................................................11 FIGURA 12.18 HERMANO NEGRO, SOBRINOS ROJOS. ...................................................................................12 Profesor Leopoldo Silva Bijit 26-05-2008