手撕红黑树插入的几种情况-(“见叔行事”)(图文代码详细版:C语言)

红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色可以是Red或Black。 通过任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

好勒,说完开场白就到了重头戏中的插入:

红黑树的插入有5种场景:

1、如果根节点为空,直接插入,不需要调整

2、如果父节点为黑色,直接插入,不需要调整

3、如果父节点为红色,叔父节点不存在,则需要变成一条直线后,把祖父节点相对应旋转,将父节点变成黑色,原来的祖父节点变成红色

4、如果父节点为红色,叔父节点为红色,则需要把父节点和叔父节点涂为黑色,祖父节点为红色(注意:把祖父节点涂为红色以后需要重新遍历祖父节点和上一层节点是否有颜色冲突,这里的处理方法在代码有详细的介绍)

5、如果父节点为红色,叔父节点为黑色,则父亲节点的其中一个子节点必然是黑色!我们直接把新插入的节点染黑即可。

为了更方便大家的理解,我们通过图和代码讲解:

1、2两种情景实在太简单,我们插入的目的是为了保持黑高的平衡,所以不做解释

首先介绍第四种情况:

由上面的两个图可以看到,我们直接需要把祖父、叔父节点和父节点的颜色转换,并继续以A作为一个新加入的节点进行判断它的父节点不为空即可,直到不满足情况为止!!

while (z->parent->color == RED) { //循环处理
        z = z->parent->parent;   //继续递归处理

第三种和第五种情况比较相似:

首先需要满足一个直线的要求,如果满足这种要求,则一切好办:

        

 

注意!!此时B的下面必定有一个黑色节点(没标字母)

在完成构成直线的操作以后,我们就判断是否存在叔父节点,如果不存在叔父节点的话,我们就把Z的祖父节点进行旋转,原本祖父节点变成红色以及父亲节点变成黑色即可。

如果像上图一样存在黑色的祖父节点,那么我们也需要旋转祖父节点的操作:

这种方法还是有点麻烦,其实直接直接把新插入的直接染黑即可!

 旋转后的效果

接下来我们只需要把z的父亲节点B变成黑色

把A变成红色

把Z变成黑色即可,这样就可以满足黑高!!

代码有更详细的讲解!!

void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {
    rbtree_node * y,*u;
    while (z->parent->color == RED) { //循环处理

        if (z->parent == z->parent->parent->left) {
            y = z->parent->parent->right; //叔父节点
        }
        else {
            y = z->parent->parent->left; //叔父节点
        }

        if (y == T->nil) {   //如果叔父节点不存的情况下
            u = z->parent->parent; //u作为接收原本的祖父节点
            if ((z == z->parent->right && z->parent != u->right) |
                (z == z->parent->left && z->parent != u->left)) {  //判断是否是一条直线,如果不是一条直线的话就需要旋转成为一条直线
                z = z->parent;
                if (z == z->parent->left)
                    rbtree_left_rotate(T, z);
                if (z == z->parent->right)
                    rbtree_right_rotate(T, z);
            }//变成直线后则可进一步操作
             //无论怎样,z还是处于最下的一层,此时我们就可以旋转z的祖父节点
            if (z == z->parent->left)
                rbtree_left_rotate(T, u); 
            if (z == z->parent->right)
                rbtree_right_rotate(T, u);
            u->color = RED;
            z->parent = BLACK;
        }

        if (y->color == RED) {  //叔父节点是红色的或者叔父不存在的时候

            z->parent->color = BLACK;
            z->parent->parent->color = RED;
            y->color = BLACK;

            z = z->parent->parent;   //继续递归处理

        } else { // 叔父节点是黑色
                if ((z == z->parent->right && z->parent!= z->parent->parent->right)|
                    (z == z->parent->left && z->parent != z->parent->parent->left)) {  //判断是否是一条直线,如果不是一条直线的话就需要旋转成为一条直线
                    z = z->parent;
                    if (z == z->parent->left)
                        rbtree_left_rotate(T, z);
                    if (z == z->parent->right)
                        rbtree_right_rotate(T, z);
                }//变成直线后则可进一步操作
                 //无论怎样,z还是处于最下的一层,此时我们就可以旋转z的祖父节点
                
                if (z->parent->left == z) {
                    rbtree_right_rotate(T, z->parent->parent);
                    z->parent->color = BLACK;
                    z->color = BLACK;
                    z->parent->right->color = RED;  //原本的祖父节点也变成红色
                }
                if (z->parent->right == z) {
                    rbtree_left_rotate(T, z->parent->parent);
                    z->parent->color = BLACK;
                    z->color = BLACK;
                    z->parent->left->color = RED;
                }  
                //此时就是z的父亲节点作为最顶层(1),z与原来的祖父节点为同一个层次(2),叔父节点就在最后一层(3)
        }
    }
    T->root->color = BLACK;   //因为不断的遍历有可能把根节点变成红色,所以最后必须保持根节点黑色,以防万一
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值