这一篇红黑树将讲解关于红黑树删除的操作,学习本内容的时候小编可是使出了吃屎的尽头~~~~,好下面开始讲解:
上代码:
节点替换的函数:
void RB_transplant(node *tree_root,node * u,node * v){
if(u->parent == NULL){
tree_root = u;
}else if(u == u->parent->left){
u->parent->left = v;
}else {
u->parent->right = v;
}
v->parent = u->parent;
}
节点删除操作的函数:
void RB_delete(node *tree_root,node *z){
node *y;
node *x;
node *search;
y = z;
search = z->right;
int original_color = y->color;
//对z是否有左右子节点分情况讨论
if(z->left ==NULL){
x = z->right;
RB_transplant(tree_root,z,z->right);
}else if(z->right ==NULL){
x= z->left;
RB_transplant(tree_root,z,z->left);
}else{
//寻找后继元素
while(search!=NULL){
y = search;
search = search->left;
}
original_color = y->color;//记录此时的颜色
x = y->right;//将y的右子树指针地址保存
//此处的处理是针对y的右侧子树来的
if(y->parent ==z){
x->parent = y;
}else{
RB_transplant(tree_root,y,y->right);//用y右侧节点代替y的节点
y->right = z->right;//将原来z右侧指针指向y的右侧节点
y->right->parent = y;//将y的右侧的父指针指向y
}
RB_transplant(tree_root,z,y);//用y来代替z的位置
//处理原来z的左侧子树的节点
y->left = z->left;
y->left->parent =y;
y->color = z->color;
}
//如果颜色为黑色,则有可能打破了红黑树的平衡,因此要进行红黑树修正的工作
if(original_color ==BLACK){
RB_delete_fixup(tree_root,x);
}
}
下面由小编我用图文并茂的方式一一解答每个部分代码的意思:
对于删除操作根据删除节点Z左右子树的存在与否可以分为三种情况:
这个是第一个if中的情况,其操作直接将z的右侧第一个节点来替换z即可。
if(z->left ==NULL){
x = z->right;
RB_transplant(tree_root,z,z->right);<span style="font-family: Arial, Helvetica, sans-serif;">}</span>
这个是第二个if中的情况,其操作直接将z的左侧第一个节点来替换z即可。
else if(z->right ==NULL){
x= z->left;
RB_transplant(tree_root,z,z->left);
}
图一
图二
图三
图四
图一是第三个elsef要处理的情况:首先通过while来寻找Z的后继y,即最小值。然后找到y的右子树节点,交给x指针来管理,如果y的父节点为z直接将x的父指针指向y即可,如果不是则进行下一步处理:见图片二 原来y的位置由y的右子树节点占据,y的右指针指向原来z的有指针指向的位置,最后将这个节点父指针指向y这样 Z的右侧子树已经移交给y,然后用y开始代替z的位置,见图片三和四,进行最后的删除操作。
else{
//寻找后继元素
while(search!=NULL){
y = search;
search = search->left;
}
original_color = y->color;//记录此时的颜色
x = y->right;//将y的右子树指针地址保存
//此处的处理是针对y的右侧子树来的
if(y->parent ==z){
x->parent = y;
}else{
RB_transplant(tree_root,y,y->right);//用y右侧节点代替y的节点
y->right = z->right;//将原来z右侧指针指向y的右侧节点
y->right->parent = y;//将y的右侧的父指针指向y
}
RB_transplant(tree_root,z,y);//用y来代替z的位置
//处理原来z的左侧子树的节点
y->left = z->left;
y->left->parent =y;
y->color = z->color;
}
上述的删除操作解释完成,最后针对颜色为黑色的时候来进行红黑树的修复工作,因为为黑色的时候打破了红黑树的平衡性,因此要进行修复工作:
那么在删除的时候会打破红黑树的哪些性质呢?
1.有可能打破性质二,当要删除的节点为根节点为黑色而其孩子为红色的时候,那么当删除根节点的时候,红色的孩子代替原来的根节点,这个时候就会打破性质二。
2.有可能打破性质四,如果原来y的父节点为红色y节点为黑色,y节点的右节x点为红色,当y移动到要删除的位置,x连接到y原来的父节点上的时候,就会出现连续2个红色节点的情况,这样就打破了性质四。如下图所示
3.有可能打破性质五,同样如上图当y移动到原来z的位置上的时候,会造成一条支路上黑色的节点丢失,破坏了整体红黑树的平衡性。
对于不知道红黑树怎么操作的可以访问这个网站,可以动态的查看红黑树的插入和删除------》》》》》》》》》》》》点击打开链接
算法导论上的讲解:
改正这一问题的办法是将现在占有y原来位置的节点x视为还有一重额外的黑色。也就是说,如果将任意包含节点x的简单路径上黑节点个数加1,则在这种假设下,性质5成立。当将黑色结点y删除或移动时,将其黑色“下推”给节点x。现在问题变为节点x可能既不是红色,又不是黑色,从而违反了性质1.现在的节点x是双重黑色或者红黑色,这就分别给包含x的简单路径上黑节点数贡献了2或1.x的color属性任然是RED(如果x是红黑色的)或者BLACK(如果x是双重黑色的)。换句话说,节点额外的黑色是针对x节点的,而不是反应在他的color属性上的。
下面分情况讨论一下删除节点所遇到的情况:
情况1:x的兄弟节点w是红色的;
代码片段:
//case1
//如果兄弟节点为红色的时候
if(w->color == RED){
w->color = BLACK;//将兄弟节点w染成黑色
x->parent->color = RED;//将x的父节点染成红色
left_rotate(tree_root,x->parent);//将x的父亲节点左旋转
}
因为w必有黑色子节点,所以可以改变w和x->parent的颜色,然后对x->parent做一次左旋转而不违反红黑树的任何性质。现在,x的新兄弟节点是旋转之前w的某个子节点,其颜色为黑色。这样将情况1转换为情况2,3或4处理。
情况2:x的兄弟节点w是黑色的,而且w的两个子节点都是黑色的;
代码片段:
//case2
//如果x的兄弟节点的左侧子节点颜色为黑且他的右侧节点也为黑的时候
if(w->left->color == BLACK && w->right->color == BLACK){
w->color = RED;//那么x的兄弟节点必须为红色,则需要将w染色成红色
x = x->parent;//然后上移x指针到其父亲指针处
因为w也是黑色的,所以从x和w上去掉一重黑色,使得x只有一重黑色而w为红色。为了补偿重x和w中去掉的一重黑色,在原来是红色或黑色的x->parent上新增一重额外的黑色。通过将x->parent作为新节点x来重复while循环。注意到,如果通过情况1进入到情况2,则新结点x是红黑色的,因为原来的x->parent是红色的。因此,新节点x的color属性值c为RED,并且在测试循环条件后循环终止。
情况3:x的兄弟节点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的;
//case3
//如果x的兄弟节点的右侧子节点颜色为黑色,w的左侧节点为红色
}else if(w->right->color ==BLACK){
w->left->color = BLACK;
w->color = RED;
right_rotate(tree_root,w);
w = x->parent->right;
}
当w为黑色且左子树节点为红色、右孩子为黑色时。可以交换w和其左孩子w->left的颜色,然后对w进行右旋转而不违反红黑树的任何性质。现在x的新兄弟节点w是一个有红色右子树节点的黑色结点,这样就将情况3转换成了情况4.
情况4:x的兄弟节点w是黑色的,w其w的右孩子是红色的;
//case4
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
left_rotate(tree_root,x->parent);
x = tree_root;
通过进行某些颜色修改并对x->parent做一次左旋转,可以去掉x的额外黑色,从而使它变为单重黑色,而且不破坏红黑树的任何性质。将x设置为根后,当while循环测试其循环条件时,循环终止。
红黑树删除操作中修改红黑树操作代码如下:
void RB_delete_fixup(node * tree_root,node *x){
node * w;
while(x!=tree_root && x->color ==BLACK){
//x在父节点的左侧的情况
if(x==x->parent->left){
//找到x的兄弟节点w
w = x->parent->right;
//case1
//如果兄弟节点为红色的时候
if(w->color == RED){
w->color = BLACK;//将兄弟节点w染成黑色
x->parent->color = RED;//将x的父节点染成红色
left_rotate(tree_root,x->parent);//将x的父亲节点左旋转
}
//case2
//如果x的兄弟节点的左侧子节点颜色为黑且他的右侧节点也为黑的时候
if(w->left->color == BLACK && w->right->color == BLACK){
w->color = RED;//那么x的兄弟节点必须为红色,则需要将w染色成红色
x = x->parent;//然后上移x指针到其父亲指针处
//case3
//如果x的兄弟节点的右侧子节点颜色为黑色,w的左侧节点为红色
}else if(w->right->color ==BLACK){
w->left->color = BLACK;
w->color = RED;
right_rotate(tree_root,w);
w = x->parent->right;
}
//case4
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
left_rotate(tree_root,x->parent);
x = tree_root;
//一下情况与上述case1~case4对称出现
}else if(x==x->parent->right){
w = x->parent->left;
if(w->color==RED){
w->color = BLACK;
x->parent->color =RED;
right_rotate(tree_root,x->parent);
}
if(w->left->color ==BLACK && w->right->color==BLACK){
w->color =RED;
x = x->parent;
}else if(w->left->color ==BLACK){
w->right->color =BLACK;
w->color =RED;
left_rotate(tree_root,w);
w = x->parent->left;
}
w->color = x->parent->color;
x->parent->color =BLACK;
x->left->color =BLACK;
right_rotate(tree_root,x->parent);
x=tree_root;
}
}
x->color = BLACK ;
}