在将红黑树中某个节点删除时,分几个步骤,首先找到该节点的位置,然后删除该节点,最后调整红黑树。
本文代码还存在一个问题没有解决,当连续删除到第3个节点时会出现问题,现在暂时还没找出问题,以后有空慢慢研究。
1、找出要删除的节点
TREE rb_delete_find(TREE r, int d) // find the node that need deleted
{
NODE *x;
x = r;
while(x != NULL)
{
if(x->key == d)
break;
else if(d < x->key)
x = x->left;
else
x = x->right;
}
if(x == NULL)
{
printf("find failed\n", x->key);
return NULL;
}
else
{
printf("find success, %d\n", x->key);
return rb_delete(r, x);
}
}
2、删除节点
在找到要删除的节点后,根据不同的情形将节点删除。代码如下:
TREE rb_delete(TREE r, NODE *z)
{
NODE *x, *y = z, *tmp;
int y_original_color = y->color;
tmp = (TREE)malloc(sizeof(NODE));
tmp->key = -1;
tmp->color = 1;
tmp->left = NULL;
tmp->right = NULL;
if(z->left == NULL)
{
x = z->right;
r = rb_delete_node(r, z, z->right);
if(x == NULL)
{
x = tmp;
x->p = z->p;
z->p->right = x;
}
}
else if(z->right == NULL)
{
x = z->left;
r = rb_delete_node(r, z, z->left);
}
else //左右孩子都不为空
{
y = tree_minimum(z->right); //y要么没有子节点要么只有右孩子
y_original_color = y->color;
x = y->right;
if(y->p != z)
{
r = rb_delete_node(r, y, y->right);
y->right = z->right;
y->right->p = y;
}
r = rb_delete_node(r, z, y);
y->left = z->left;
y->left->p = y;
y->color = z->color;
if(NULL == x)
{
x = tmp;
x->p = y;
y->right = x;
}
}
// printf("root->key: %d \tx->key: %d\n", r->key, x->key);
// printf("root->left: %d \troot->right: %d\n", r->left->key, r->right->key);
/**在删除或移动黑色节点时,需要将他的黑色下推给他的孩子节点。若孩子节点为空,
这时将无法将黑色下推。若黑色向上推则会导致黑高的不相等。
在此通过创建临时节点,将其作为叶子节点。叶子节点的颜色为黑色,值为-1.
**/
//printf("------------r.key = %d x.key = %d\n",r->key, x->key);
//y_original_color保存了移动或删除的节点的颜色,当移动或删除的节点为红色时,红黑树的性质没有被破坏,
//而当移动或删除的节点为黑色时,红黑树的性质被破坏了,这是就需要对其进行调整。
if(y_original_color == 1) //1:黑色, 0:红色
{
r = rb_delete_fixup(r, x);
}
return r;
}
3、红黑树的调整
当删除或移动的节点为黑色时,需要对红黑树进行调整,以使得其继续保持红黑树的性质。具体的调整过程见上一篇文章。
调整代码如下:
TREE rb_delete_fixup(TREE r, NODE *z)
{
NODE *x = z, *w;
while(x!=r && x->color==1)
{
if(x == x->p->left) //x is left child
{
w = x->p->right;
if(w!=NULL && w->color == 0) //case 1: w->color=0
{
w->color = 1;
x->p->color = 0;
r = left_rotate(r, x->p);
w = x->p->right;
}
//提取x和w的一个黑色, 上移到x的父节点。从w提出一个黑色后其变成了红色,x还剩一个黑色
if((w->left==NULL ||w->left->color==1) && (w->right==NULL || w->right->color==1)) //case 2: w->color=1 && w.left=1 && w.right=1
{
w->color = 0;
x = x->p;
}
else
{
if(w->right==NULL || w->right->color==1) //case 3: w->color=1 && w.left=0 && w.right=1
{
w->left->color = 1;
w->color = 0;
r = right_rotate(r, w);
w = x->p->right;
}
w->color = x->p->color; //case 4: w->color=1 && w.right=0
x->p->color = 1;
w->right->color = 1;
r = left_rotate(r, x->p);
x = r;
}
}
else //x is right child
{
w = x->p->left;
if(w->color == 0) //case 1
{
w->color = 1;
x->p->color = 0;
r = right_rotate(r, x->p);
w = x->p->left;
}
if((w->left==NULL || w->left->color==1) && (w->right==NULL || w->right->color==1)) //case 2
{
w->color = 0;
x = x->p;
}
else
{
if(w->left==NULL || w->left->color==1) //case 3
{
w->color = 0;
w->right->color = 1;
r = left_rotate(r, w);
w = x->p->left;
}
//case 4
w->color = x->p->color;
w->left->color = 1;
x->p->color = 1;
r = right_rotate(r, x->p);
x = r;
}
}
}
x->color = 1;
return r;
}
在以上的过程中还用到了以下函数,
首先是删除节点函数,其代码如下:
/**
** 删除节点x,使用y来代替x节点
**/
TREE rb_delete_node(TREE r, NODE *x, NODE *y)
{
if(x->p == NULL)
{
r = y;
}
else if(x == x->p->left)
{
x->p->left = y;
}
else
x->p->right = y;
if(y != NULL)
y->p = x->p;
return r;
}
其次是查找后继节点的函数,代码如下:
//查找节点z的后继节点
NODE *tree_minimum(NODE *z)
{
NODE *x = z;
while(x->left != NULL)
x = x->left;
return x;
}
其他的如节点的数据结构,树的左旋和右旋的具体实现函数,见红黑树的插入那篇文章。