数据结构- 红黑树插入和调整

前面的提到了红黑树的结构定义还有旋转,其中包括左旋和右旋, 那么接下来就是插入并且做调整。

一: 插入逻辑

  • 从根节点出发,若根节点是空节点,则根节点为插入节点
  • 根节点不为空,则遍历树,以key值为大小,直到空节点,返回空节点的父节点, 遍历结束;
  • 若插入节点的key小于父节点的key, 则插入父节点的左子树, 并让插入节点的左右子树指向空节点
  • 若插入节点的key小于父节点的key, 则插入父节点的右子树, 并让插入节点的左右子树指向空节点
  • 插入节点的颜色为红色

这里的处理逻辑就是不断遍历左右子树,然后找到最下面的nil节点为止,那么其上的父节点就是要考虑的插入位置,最后再考虑一下左右即可。

注意插入的节点默认都是红色,保证其他子树的黑色数量一致,也不违背根节点是黑色等条件,但是可能违背父子节点不能是红色,因此后续需要调整。

void rbtree_insert(rbtree_t *T, rbtree_node_t *z) {
    //遍历的节点
    rbtree_node_t *x = T->root;

    //y用于保存要插入的位置的父节点
    rbtree_node_t *y = T->nil;

    while(x != T->nil) {
        y = x;
        if (x->key > z->key){
            x = x->left;
        }else if(x->key < z->key){
            x = x->right;
        }else{
            //Exist
            return;
        }
    }

    if (y== T->nil) {
        //空树
        T->root = z;
    } else {
        if (y->key > z->key) {
             y->left = z;
        } else {
            y->right = z;
        }
    }
   
    z->parent = y;

    //下面设置z属性
    z->left = T->nil;
    z->right = T->nil;
    //插入的颜色默认是红色,保证不会改变黑色节点的路径
    //但是可能不满足父子节点不能同时为红色的限制,因此后续需要考虑调整
    z->color = RED;

    rbtree_insert_fixup(T, z);
}

二: 节点调整

对于节点的调整,因为插入节点是红色的,因此只有在其父节点也是红色的情况下才需要调整。同时其祖父节点(即父节点的父节点)一定是黑色的,但是祖父节点的另一个子节点,即叔父节点的颜色则是不确定的,因此可以从叔父节点的颜色入手考虑。

2.1. 叔父节点也是红色

这里要调整就是祖父节点和其子节点颜色对换即可,方式如下所示:

 那么这里的简易处理逻辑如下:

// rbtree_node_t *z 是插入的节点


//叔父节点
rbtree_node_t *y = z->parent->parent->right;

if (y->color == RED) {
    z->parent->color = BLACK;
    y->color = BLACK;
    z->parent->parent->color =RED;
 
    //回溯z,保证回溯到z都是红色的
    z = z->parent->parent;
}

要注意这里最后 z = z->parent->parent 这段代码,这里是因为下面的子树解决了,但是上层可能又有冲突,例如祖父节点的父节点也是红色,因此这时候还要继续迭代考虑。

2.2. 叔父节点是黑色

对于叔父节点是黑色的时候,这里还要分情况, 因为要分左右子树的情况,下面是左子树的情况:

这里迭代到z节点,然后和父节点都说红色并且在左子树。那么这里需要做两步:

  1. z的父节点和祖父节点颜色对换
  2. 以z的祖父节点(红色)进行右旋

下面是右子树的情况:

这里要做的事情有两件:

  1. 把z节点改为其父节点
  2. 以新的z节点进行左旋

这时候得到的新的树,就是第一种情况,也就是父子节点都是红色的,并且在左子树。在下次递归的时候就可以进行处理了。

下面是调整函数的完整代码:

void rbtree_insert_fixup(rbtree *T, rbtree_node_t *z)
{

    // 父子节点都是红色的, 祖父节点必然是黑色的
    // 但是叔父节点(即祖父节点的另一个子节点)颜色未知
    if (z->color == RED)
    {
        while (z->parent->color == RED)
        {
            if (z->parent->parent->left == z->parent)
            {
                // 父节点在祖父节点的左子树下

                // 叔父节点
                rbtree_node_t *y = z->parent->parent->right;

                if (y->color == RED)
                {
                    // 叔父节点也是红色的  -->  祖父节点和其子节点颜色对调

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

                    // 回溯z,保证回溯到z都是红色的
                    z = z->parent->parent;
                }
                else
                {
                    // 叔父节点是黑色的  --> 区分z是在左还是右子树

                    if (z == z->parent->left)
                    {
                        // z节点在左子树下

                        // 1).对换父节点和祖父节点颜色
                        z->parent->color = BLACK;
                        z->parent->parent->color = RED;

                        // 2). 以祖父节点进行右旋
                        rbtree_right_roate(T, z->parent->parent);
                    }
                    else
                    {
                        // z节点在右子树下

                        // 1). 把z改为其父节点
                        z = z->parent;
                        // 2). 进行左旋
                        rbtree_left_roate(T, z);
                    }
                }
            }
            else
            {
                // 叔父节点
                rbtree_node_t *y = z->parent->parent->left;

                if (y->color == RED)
                {
                    // 叔父节点也是红色的  -->  祖父节点和其子节点颜色对调

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

                    // 回溯z,保证回溯到z都是红色的
                    z = z->parent->parent;
                }
                else
                {
                    //和上面的情况对比,则是左右对调, 左右旋互换
                     if (z == z->parent->right)
                    {
                        
                        z->parent->color = BLACK;
                        z->parent->parent->color = RED;

                        
                        rbtree_left_roate(T, z->parent->parent);
                    }
                    else
                    {
                        z = z->parent;
                        rbtree_right_roate(T, z);
                    }
                }
            }
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值