Eliminar en un Red-Black Tree comienza como un borrado de BST y luego corrige la posible pérdida de un nodo negro. El objetivo es preservar la altura negra uniforme y evitar rojos consecutivos.
Se sigue el procedimiento estándar de un árbol de búsqueda:
hijo y hereda la posible deuda de color.Si el nodo eliminado era negro y el hijo que lo reemplaza también es negro (o NIL negro), aparece un doble negro que reduce en 1 la altura negra de los caminos por ese lado.
El recoloreo resuelve los casos en que el hermano es negro y tiene al menos un hijo rojo o cuando el hermano es rojo:
Las rotaciones reubican al hermano y al padre para redistribuir la altura negra:
void arreglarEliminacion(RbNode **raiz, RbNode *x) {
while (x != *raiz && x->color == NEGRO) {
int xEsIzq = (x == x->padre->izquierdo);
RbNode *h = xEsIzq ? x->padre->derecho : x->padre->izquierdo;
if (h->color == ROJO) {
h->color = NEGRO;
x->padre->color = ROJO;
if (xEsIzq) rotarIzquierda(raiz, x->padre);
else rotarDerecha(raiz, x->padre);
h = xEsIzq ? x->padre->derecho : x->padre->izquierdo;
}
if (h->izquierdo->color == NEGRO && h->derecho->color == NEGRO) {
h->color = ROJO;
x = x->padre;
} else {
if (xEsIzq && h->derecho->color == NEGRO) {
h->izquierdo->color = NEGRO;
h->color = ROJO;
rotarDerecha(raiz, h);
h = x->padre->derecho;
} else if (!xEsIzq && h->izquierdo->color == NEGRO) {
h->derecho->color = NEGRO;
h->color = ROJO;
rotarIzquierda(raiz, h);
h = x->padre->izquierdo;
}
h->color = x->padre->color;
x->padre->color = NEGRO;
if (xEsIzq) {
h->derecho->color = NEGRO;
rotarIzquierda(raiz, x->padre);
} else {
h->izquierdo->color = NEGRO;
rotarDerecha(raiz, x->padre);
}
x = *raiz;
}
}
x->color = NEGRO;
}
Ejemplo autocontenible con nodo NIL compartido, inserción y eliminación con correcciones. Copia en main.c y ejecútalo.
#include <stdio.h>
#include <stdlib.h>
typedef enum Color { ROJO, NEGRO } Color;
typedef struct RbNode {
int clave;
Color color;
struct RbNode *izq;
struct RbNode *der;
struct RbNode *padre;
} RbNode;
RbNode *NIL;
RbNode *crearNodo(int clave, RbNode *padre) {
RbNode *n = malloc(sizeof(RbNode));
n->clave = clave;
n->color = ROJO;
n->izq = n->der = NIL;
n->padre = padre ? padre : NIL;
return n;
}
void rotarIzquierda(RbNode **raiz, RbNode *x) {
RbNode *y = x->der;
x->der = y->izq;
if (y->izq != NIL) y->izq->padre = x;
y->padre = x->padre;
if (x->padre == NIL) *raiz = y;
else if (x == x->padre->izq) x->padre->izq = y;
else x->padre->der = y;
y->izq = x;
x->padre = y;
}
void rotarDerecha(RbNode **raiz, RbNode *y) {
RbNode *x = y->izq;
y->izq = x->der;
if (x->der != NIL) x->der->padre = y;
x->padre = y->padre;
if (y->padre == NIL) *raiz = x;
else if (y == y->padre->izq) y->padre->izq = x;
else y->padre->der = x;
x->der = y;
y->padre = x;
}
void arreglarInsercion(RbNode **raiz, RbNode *n) {
while (n->padre != NIL && n->padre->color == ROJO) {
RbNode *p = n->padre;
RbNode *g = p->padre;
int pEsIzq = (p == g->izq);
RbNode *opuesto = pEsIzq ? g->der : g->izq;
if (opuesto->color == ROJO) {
p->color = NEGRO;
opuesto->color = NEGRO;
g->color = ROJO;
n = g;
continue;
}
if (pEsIzq && n == p->der) {
rotarIzquierda(raiz, p);
n = p;
p = n->padre;
} else if (!pEsIzq && n == p->izq) {
rotarDerecha(raiz, p);
n = p;
p = n->padre;
}
p->color = NEGRO;
g->color = ROJO;
if (pEsIzq) rotarDerecha(raiz, g);
else rotarIzquierda(raiz, g);
}
(*raiz)->color = NEGRO;
}
RbNode *insertar(RbNode *raiz, int clave) {
RbNode *padre = NIL;
RbNode *cur = raiz;
while (cur != NIL) {
padre = cur;
if (clave < cur->clave) cur = cur->izq;
else if (clave > cur->clave) cur = cur->der;
else return raiz;
}
RbNode *nuevo = crearNodo(clave, padre);
if (padre == NIL) raiz = nuevo;
else if (clave < padre->clave) padre->izq = nuevo;
else padre->der = nuevo;
arreglarInsercion(&raiz, nuevo);
return raiz;
}
RbNode *minimo(RbNode *n) {
while (n->izq != NIL) n = n->izq;
return n;
}
void trasplantar(RbNode **raiz, RbNode *u, RbNode *v) {
if (u->padre == NIL) *raiz = v;
else if (u == u->padre->izq) u->padre->izq = v;
else u->padre->der = v;
v->padre = u->padre;
}
void arreglarEliminacion(RbNode **raiz, RbNode *x) {
while (x != *raiz && x->color == NEGRO) {
int xEsIzq = (x == x->padre->izq);
RbNode *h = xEsIzq ? x->padre->der : x->padre->izq;
if (h->color == ROJO) {
h->color = NEGRO;
x->padre->color = ROJO;
if (xEsIzq) rotarIzquierda(raiz, x->padre);
else rotarDerecha(raiz, x->padre);
h = xEsIzq ? x->padre->der : x->padre->izq;
}
if (h->izq->color == NEGRO && h->der->color == NEGRO) {
h->color = ROJO;
x = x->padre;
} else {
if (xEsIzq && h->der->color == NEGRO) {
h->izq->color = NEGRO;
h->color = ROJO;
rotarDerecha(raiz, h);
h = x->padre->der;
} else if (!xEsIzq && h->izq->color == NEGRO) {
h->der->color = NEGRO;
h->color = ROJO;
rotarIzquierda(raiz, h);
h = x->padre->izq;
}
h->color = x->padre->color;
x->padre->color = NEGRO;
if (xEsIzq) {
h->der->color = NEGRO;
rotarIzquierda(raiz, x->padre);
} else {
h->izq->color = NEGRO;
rotarDerecha(raiz, x->padre);
}
x = *raiz;
}
}
x->color = NEGRO;
}
RbNode *buscar(RbNode *n, int clave) {
while (n != NIL && clave != n->clave) {
n = (clave < n->clave) ? n->izq : n->der;
}
return n;
}
RbNode *eliminar(RbNode *raiz, int clave) {
RbNode *z = buscar(raiz, clave);
if (z == NIL) return raiz;
RbNode *y = z;
Color yColor = y->color;
RbNode *x;
if (z->izq == NIL) {
x = z->der;
trasplantar(&raiz, z, z->der);
} else if (z->der == NIL) {
x = z->izq;
trasplantar(&raiz, z, z->izq);
} else {
y = minimo(z->der);
yColor = y->color;
x = y->der;
if (y->padre == z) {
x->padre = y;
} else {
trasplantar(&raiz, y, y->der);
y->der = z->der;
y->der->padre = y;
}
trasplantar(&raiz, z, y);
y->izq = z->izq;
y->izq->padre = y;
y->color = z->color;
}
if (yColor == NEGRO) {
arreglarEliminacion(&raiz, x);
}
free(z);
return raiz;
}
void imprimir(RbNode *n, int nivel) {
if (n == NIL) return;
imprimir(n->der, nivel + 1);
for (int i = 0; i < nivel; ++i) printf(" ");
printf("%d(%c)\n", n->clave, n->color == ROJO ? 'R' : 'N');
imprimir(n->izq, nivel + 1);
}
int main(void) {
RbNode nilNode = {0, NEGRO, NULL, NULL, NULL};
NIL = &nilNode;
RbNode *raiz = NIL;
int datos[] = {20, 15, 25, 10, 18, 22, 30, 5, 12, 17, 19};
int n = (int)(sizeof(datos) / sizeof(datos[0]));
for (int i = 0; i < n; ++i) {
raiz = insertar(raiz, datos[i]);
}
printf("Inicial:\n");
imprimir(raiz, 0);
int borrar[] = {10, 22, 20};
int m = (int)(sizeof(borrar) / sizeof(borrar[0]));
for (int i = 0; i < m; ++i) {
raiz = eliminar(raiz, borrar[i]);
printf("\nTras eliminar %d:\n", borrar[i]);
imprimir(raiz, 0);
}
return 0;
}