红黑树插入结点方法`balanceInsertion`的源码分析

红黑树插入结点方法balanceInsertion的源码分析

红黑树的定义:

性质1. 节点是红色或黑色。

性质2. 根节点是黑色。

性质3.所有叶子都是黑色。(叶子是NUIL节点)

性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

性质5… 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

在这里插入图片描述

这个图里面没有画出叶结点(空结点),叶结点就是结点如果没有左右结点指向,就会指向叶结点

红黑树平衡:每一个结点到其各个子孙结点的叶结点的路径,经历的黑色结点的个数是一样的。比如,图中27到其两个叶结点经历的黑色结点个数是两个,其中包括自己和叶结点。而根结点50,到其所有的子孙结点的叶结点所经历的黑色结点个数都是三个。而红色的结点则是为了平衡黑色结点

插入的所有情况:

插入的时候如果父结点是黑色的,则不会对红黑树所有的性质产生影响,不要操作,而当父结点是红色的时候,则会对红黑树的性质有影响,则需要进行许多操作。插入操作是三个一组,当前结点,父结点和祖父结点。完成一组操作后还要向上检查是否满足红黑树性质。

父结点是红色:

情况一到三都是父结点在祖父结点的左结点

情况一:叔结点,就是父结点的同一级不为空并且是红色。

将叔结点和父结点都转换为黑色就可以满足红黑树

情况二:叔结点为空,父结点是祖父结点的左结点,结点是父结点的右结点

需要将父结点左旋,然后进入下一次循环的情况三

情况三:叔结点为空,结点在父结点的左边,父结点在祖父结点的左边

父结点变成黑色,祖父结点变成红色然后将祖父结点右旋

情况四到六都是父结点在祖父结点的右结点

情况四:叔结点不为空并且是红色

将叔结点和父结点都转换为黑色就可以满足红黑树

情况五:叔结点为空,如果结点是父结点的左结点(父结点是祖父结点的右结点)

把父结点右旋,再重新进入循环进入情况六

情况六:叔结点为空,父结点是祖父节点的右节点,当前节点也是父节点的右节点

将父结点变成黑色,把祖父结点左旋

static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                            TreeNode<K,V> x) {
    //结点的颜色默认是红色的
    x.red = true;
    //xp父结点 xpp祖父结点  xppl祖父结点的左结点 xppr祖父结点的右结点
    //循环是因为要循环判断在旋转或者变色之后的结构还是否满足红黑树规则,一直到根结点
    for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
        //如果这里是根节点,则让根结点变成黑色,返回红黑树
        if ((xp = x.parent) == null) {
            x.red = false;
            return x;
        }
        //如果当前结点的上一个结点是黑色的,就停止循环
        //因为如果父结点是黑色的,则插入红色结点不会对黑节点的平衡产生影响
        else if (!xp.red || (xpp = xp.parent) == null)
            return root;
        //判断父结点是不是祖父结点的左结点
        if (xp == (xppl = xpp.left)) {
            //情况一:判断叔结点,就是父结点的同一级不为空并且是红色的
            /*
            执行,叔结点和父结点换成黑色,祖父结点换成红色
            */
            if ((xppr = xpp.right) != null && xppr.red) {
                xppr.red = false;
                xp.red = false;
                xpp.red = true;
                x = xpp;//将当前结点换为祖父结点继续判断是不是符合红黑树规则
            }
            else {
                //情况二:父结点是祖父结点的左结点,结点是父结点的右结点
                /*
                执行,将父结点和root左旋,然后重新执行循环
                */
                if (x == xp.right) {
                    root = rotateLeft(root, x = xp);
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                //情况三:经过情况二,结点在父结点的左边,父结点在祖父结点的左边
                //如果当前的父结点不为空,让其变为黑色
                if (xp != null) {
                    xp.red = false;
                    //如果祖父结点不为空,让其变为红色,再对祖父结点右旋
                    /*
                    右旋之后的情况:祖父结点 -->祖父结点的右结点
                                 当前结点 -->祖父结点的左结点
                                 父结点   -->祖父结点
                    右旋之后,还需要继续循环判断是否对上面的结点造成影响
                    */
                    if (xpp != null) {
                        xpp.red = true;
                        root = rotateRight(root, xpp);
                    }
                }
            }
        }
        //此时父结点是祖父结点的右结点
        else {
            //情况四:叔结点不为空并且是红色
            /*
            将叔结点和父结点变成黑色,祖父结点变成红色,继续循环判断
            */
            if (xppl != null && xppl.red) {
                xppl.red = false;
                xp.red = false;
                xpp.red = true;
                x = xpp;
            }
            //此时叔结点为空
            //或者 叔结点是黑色的,这种情况是下面的结点调整之后,祖父结点变成红色,会造成这种情况
                  一般是循环判断的时候出现
            else {
                //情况五:如果结点是父结点的左结点(父结点是祖父结点的右结点),右旋,
                //再重新进入循环进入情况六
                /*
                右旋:主要是为了转换为这样的情况:父结点是右结点,当前节点也是右节点
                     当前节点-->父结点(祖父节点的右节点)
                     父结点-->父结点的右节点
                     祖父节点-->不变
                */
                if (x == xp.left) {
                    root = rotateRight(root, x = xp);
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                //情况六:父结点是祖父节点的右节点,当前节点也是父节点的右节点
                //将父结点变成黑色,把祖父结点左旋
                if (xp != null) {
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;
                        root = rotateLeft(root, xpp);
                    }
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值