手撕红黑树插入的几种情况-(“见叔行事”)(图文代码详细版:C语言)_冯五号的博客-CSDN博客
前文再续:在经历过插入的修复工作后,如果需要进行系列的删除工作则需要判断一下的几种情况。
在解决红黑树的删除问题之前,我们先了解下二叉搜索树的删除节点的方法:(设X为需要删除的节点)
如果需要删除的节点的节点中有N个子节点;
N:0 可以直接删除
N:1 直接把后续节点连接到要删除节点的父亲节点
N:2 需要往右走一步找到左边的孙子节点(y=x->right->left),
while ((y->left!=T->nil)&& (y->right != T->nil))
{
y = y->left;
temp.key = y->key;
y->key = x->key;
x->key = temp.key;
x = y;
y = y->right;
}//经过这个循环后,需要删除的节点的值已经挪到只连接一个节点或者无连接节点的节点上
接下来我们就可以按照N等于1或者等于0的操作进行删除(T->nil ==NULL)
在红黑树的删除过程中,经过上述的操作后,我们的X的左节点肯定是NULL。我们会遇到两种情况,当前需要删除的节点就只有一个子节点或者没有子节点
当该子节点只有1个的时候,那么该节点必定是红色节点,而X节点肯定是黑色(满足红黑树的第五条性质,如果看不懂,请看顶置的博客),那么我们直接把x的子节点改为黑色,而把子节点直接连到x的父亲节点即可:
if ((x->left != T->nil)&&(x->color == RED)) { //如果是红色的节点,不影响黑高,可以直接删除
x->left->color = BLACK; //如果是X是黑色,那么直接删除后
x->left->parent = x->parent; //把后续的子节点改为黑色,则可平衡
free(x);
return;
}
if ((x->right != T->nil) && (x->color == RED)) {
x->right->color = BLACK;
x->right->parent = x->parent;
free(x);
return;
}
if (x->color==RED) { //红色直接删除
free(x);
return;
}
接下来就剩下黑色单节点的情况了,分别是:
黑兄弟,右红侄,只有侄子是红色这个情况,如果一旦侄子有两个,并且是一红一黑,则不满足黑高情况![](https://img-blog.csdnimg.cn/dcb783c15a8c45509be1d13168e845a3.png)
//1、黑兄弟,右红侄,只有侄子是红色这个情况,如果一旦侄子有两个,并且是一红一黑,则不满足黑高情况
//
//在这个情况下,父亲节点肯定是一个红色,并且父亲、兄弟、侄子,形成一条直线
//
//解决思路:左旋父,祖染父色,父叔染黑
u = x->parent->right; //u代表是的兄弟节点
if ((u->right == RED)&&(u->left==T->nil)&&(u->color == BLACK)) {
rbtree_left_rotate(T, x->parent);//左旋父
u->color = x->parent->color; //祖染父色
x->parent->color = BLACK;
u->right->color = BLACK;
free(x);
}
那么情况1中的x->parent有没有可能是黑色呢?,答案是肯定的(如图),所以我们在这里得注意,在旋转以后,x的祖父节点,一定要用x的父节点进行给予颜色,然后再进行叔叔和父亲染成黑色。
//2、黑兄弟,左红侄
//右旋兄,交换兄弟与其右子颜色,变成情况1
//2、黑兄弟,左红侄
//右旋兄,交换兄弟与其右子颜色,变成情况1
if ((u->left == RED) && (u->right == T->nil) && (u->color == BLACK)) {
rbtree_right_rotate(T, u);
u->parent->color = BLACK;
u->color = RED;
//经历上面三步即可变成情况1
u = u->parent;
rbtree_left_rotate(T, x->parent);//左旋父
u->color = x->parent->color; //祖染父色
x->parent->color = BLACK;
u->right->color = BLACK;
free(x);
}
//3、黑兄弟,无双侄
//解决方法,直接把兄弟变成红色即可,但注意需要判断父节点是否是红色
if ((u->color == BLACK) && (u->left == T->nil) && (u->right == T->nil)) {
free(x);
u->color = RED;
if (u->parent->color == RED) //直接修复,把叔父节点当做是一个新插入的节点修复即可
rbtree_insert_fixup(rbtree * T, rbtree_node * u)
这里的操作比较粗暴,我们直接把叔父变成是一个新插入的红色节点,直接修复即可(能不能不直接修复我们直接把父节点染黑?答案是不行的,因为我们根本不知道父亲节点的兄弟黑高多少,不能贸贸然改变这边的黑高)
//4、红兄弟(则父节点肯定为黑)
//此时红兄弟下面肯定有两个黑色节点,为了保持黑高!!
//解决方法:左旋父,父祖交换颜色,后删除
if (u->color == RED) {
rbtree_left_rotate(T, x->parent); // 左旋父
x->parent->parent->color = BLACK;
x->parent->color = RED; //父祖交换颜色
free(x);
}
接下来把整体的代码给贴一遍:手撸不易麻烦大家点点收藏跟赞!!谢谢!
void rbtree_delete(rbtree* T, rbtree_node* x) {
rbtree_node* y ,*u;
rbtree_node temp;
y = x->right;
while ((y->left!=T->nil)&& (y->right != T->nil))
{
y = y->left;
temp.key = y->key;
y->key = x->key;
x->key = temp.key;
x = y;
y = y->right;
}//经过这个循环后,需要删除的节点的值已经挪到只连接一个节点或者无连接节点的节点上
if ((x->left != T->nil)&&(x->color == RED)) { //如果是红色的节点,不影响黑高,可以直接删除
x->left->color = BLACK; //如果是X是黑色,那么直接删除后
x->left->parent = x->parent; //把后续的子节点改为黑色,则可平衡
free(x);
return;
}
if ((x->right != T->nil) && (x->color == RED)) {
x->right->color = BLACK;
x->right->parent = x->parent;
free(x);
return;
}
//如果删除的节点是没有左右子节点,那么需要判断几种情况
if (x->color==RED) { //红色直接删除
free(x);
return;
}
//接下来就剩下黑色单节点的情况了,分别是:
//1、黑兄弟,右红侄,只有侄子是红色这个情况,如果一旦侄子有两个,并且是一红一黑,则不满足黑高情况
//
//在这个情况下,父亲节点肯定是一个红色,并且父亲、兄弟、侄子,形成一条直线
//
//解决思路:左旋父,祖染父色,父叔染黑
u = x->parent->right; //u代表是的兄弟节点
if ((u->right == RED)&&(u->left==T->nil)&&(u->color == BLACK)) {
rbtree_left_rotate(T, x->parent);//左旋父
u->color = x->parent->color; //祖染父色
x->parent->color = BLACK;
u->right->color = BLACK;
free(x);
}
//2、黑兄弟,左红侄
//右旋兄,交换兄弟与其右子颜色,变成情况1
if ((u->left == RED) && (u->right == T->nil) && (u->color == BLACK)) {
rbtree_right_rotate(T, u);
u->parent->color = BLACK;
u->color = RED;
//经历上面三步即可变成情况1
u = u->parent;
rbtree_left_rotate(T, x->parent);//左旋父
u->color = x->parent->color; //祖染父色
x->parent->color = BLACK;
u->right->color = BLACK;
free(x);
}
//3、黑兄弟,无双侄
//解决方法,直接把兄弟变成红色即可,但注意需要判断父节点是否是红色
if ((u->color == BLACK) && (u->left == T->nil) && (u->right == T->nil)) {
free(x);
u->color = RED;
if (u->parent->color == RED) //直接修复,把叔父节点当做是一个新插入的节点修复即可
rbtree_insert_fixup(rbtree * T, rbtree_node * u)
}
//4、红兄弟(则父节点肯定为黑)
//此时红兄弟下面肯定有两个黑色节点,为了保持黑高!!
//解决方法:左旋父,父祖交换颜色,后删除
if (u->color == RED) {
rbtree_left_rotate(T, x->parent); // 左旋父
x->parent->parent->color = BLACK;
x->parent->color = RED; //父祖交换颜色
free(x);
}
}