算法导论---红黑树

红黑树

红黑树里,对任意节点到达任意的叶节点,没有一条路径会比其他任一条路径长出2倍。

红黑树是一种特殊的二叉搜索树,有以下性质:
1. 每个结点或是红色的,或是黑色的。
2. 根结点是黑色的。
3. 每个叶结点是黑色的
4. 如果一个结点是红色的,则它的两个孩子都是黑色的
5. 从任意一个结点出发到叶结点,每条路径的黑高相等。

左旋右旋

左旋:父节点成为左孩子的右节点,原来左孩子的右节点成为父节点的左孩子
右旋:父节点成为右孩子的左节点,原来右孩子的左节点成为父节点的右孩子

    LEFT-ROTATE(T, x)
        y = x.right         //y成为x的右孩子
        x.right = y.left    //使y的左子树成为x的右子树
        if y.left != T.nil  
            y.left.p = x
        y.p = x.p           //关联x的父节点给y
        if x.p == T.nil
            T.root = y
        elseif x == x.p.left
            x.p.left = y
        else 
            x.p.right = y
        y.left = x          //x变为y的左孩子 
        x.p = y

对于旋转前后的两颗树,使用中序遍历将可输出相同的data序列。

插入

    RB_INSERT(T, z)
        y = T.nil
        x = T.root
        while x != T.nil
            y = x
            if z.key < x.key
                x = x.left
            else
                x = x.right
        z.p = y

        if y == T.nil
            T.root = z
        else if z.key < y.key
            y.left = z
        else 
            y.right = z
        z.left = T.nil
        z.right = T.nil
        z.color = RED
        RB_INSERT_FIXUP(T, z)

如果插入节点的父节点黑色,直接插入。否则,插入节点修正分为以下3中情况(分析情况均以z的父节点是z祖父的左孩子)

情况1:z的叔节点y是红色

将z的父亲与叔节点染成黑色,祖父染成红色,将z赋值为z的祖父,while循环继续

情况2:z的叔节点y是黑色,且z是父节点的右孩子

将z设为z的父节点,对其进行左旋,转换成情况3
情况3:z的叔节点y是黑色,且z是父节点的左孩子
将z的父节点染成黑色,祖父节点染成红色,对祖父节点进行右旋

    RB_INSERT_FIXUP(T, z)
        while z.p.color == RED
            if z.p == z.p.p.left
                y = z.p.p.right         //yz的叔结点
                if y.color == RED
                    z.p.color = BLACK   //1
                    y.color = BLACK     //1
                    z.p.p.color = RED   //1 保持了红黑树性质5
                    z = z.p.p           //1
                elseif z == z.p.right   //隐含了y.color == black
                    z = z.p             //2
                    LEFT_ROTATE(T, z)   //2 因为最终要进行右旋,所以让出位置
                else                    //隐含了y.color == black z.p.left
                    z.p.color = BLACK       //3
                    z.p.p.color = RED       //3
                    RIGHT_ROTATE(T, z.p.p)  //3

            //z.pz.p.p的右孩子时,处理与上面相似
            else                        //z.p == z.p.p.right
                y = z.p.p.left
                if y.color == RED
                    z.p.color = BLACK   //1
                    y.color = BLACK     //1
                    z.p.p.color = RED   //1 保持了红黑树性质5
                    z = z.p.p           //1
                elseif z == z.p.left    //隐含了y.color == black
                    z = z.p             //2
                    RIGHT_ROTATE(T, z)  //2
                else
                    z.p.color = BLACK       //3 增加了 一个黑色
                    z.p.p.color = RED       //3
                    LEFT_ROTATE(T, z.p.p)   //3 保持了性质5

        T.root.color = BLACK        

删除

与n个结点的红黑树上的其他基本操作相同,删除一个结点要花费O(lg n)时间。
更换子树的方法

    RB_TRANSPLANT(T, u, v)
    //将v结点替代u结点
        if u.p == T.nil
            T.root = v
        elseif u = u.p.left
            u.p.left = v 
        else
            u.p.right = v
        v.p = u.p
    RB_DELETE(T, z)
        y = z
        y_original_color = y.color
        if z.left == T.nil
            x = z.right
            RB_TRANSPLANT(T, z, z.right)
        elseif z.right == T.nil
            x = z.left
            RB_TRANSPLANT(T, z, z.left)
        else
            y = TREE_MAXINUM(z.right)
            y_original_color = y.color
            x = y.right
            if y.p = z
                x.p = y
            else
                RB_TRANSPLANT(T, y, y.right)    //equals to RB_TRANSPLANT(T, y, x)
                y.right = z.right
                y.right.p = y
            RB_TRANSPLANT(T, z, y)
            y.left = z.left
            y.left.p = y
            y.color = z.color           //z结点的颜色被保留,而后继y的颜色丢弃,这样做减少破坏范围
        if y_original_color == BLACK    //如果y是黑色,要修正
            RB_DELETE_FIXUP(T, x)       //x其实是需要保留一重额外黑色的结点

如果y是红色,则

  1. 根节点仍为黑色
  2. 不存在两个相邻的红结点
  3. 性质5不受影 响

如果y是黑色,则:

  1. 根节点可能变成红色
  2. x与x.p可能同时为红色
  3. y所有的祖先都违反了性质5

红黑树的删除就是要设法留住黑点。当被删除的点或其遗孤是红点时不需太多考虑

删除结点后修正分为4种情况(下列分析均以x = x.p.left为前提,相反情况处理相似)

情况1:x的兄弟结点w是红色

断定x的父节点必是黑色,将w染黑色,父节点染红色和对父节点左旋,转变成情况234

情况2:x的兄弟w是黑色,w左右孩子也是黑色

直接将w染成红色,将新的x设定为原x的父节点,该情况是唯一使while循环继续,若是从情况1进入情况2,则下次循环将结束

情况3:x的兄弟w是黑色,w左孩子是红色,右孩子是黑色

将w然红,w的左孩子染黑,对w进行右旋,转换成情况4

情况4:x的兄弟w是黑色,w右孩子是红色。

将w染成父节点的颜色,w的父节点和右孩子染成黑色,对父节点左旋

RB_DELETE_FIXUP(T, x)
    //while循环目标是将额外的黑色沿树上移,x指向的节点额外多一重黑色
    while x != T.root and x.color == BLACK  //若果x.color == RED 那么直接染成黑色
        if x == x.p.left
            w = x.p.right
            if w.color == RED           //此时,x与w的parent必定是黑色的,性质4
                w.color = BLACK         //case1 无论如何,w.color234中必定是black
                x.p.color = RED         //case1
                LEFT_ROTATE(T, x.p)     //case1
                w = x.p.right           //case1
            if w.left.color == BLACK and w.right.color == BLACK
                w.color = RED           //case2
                x = x.p                 //case2
            elseif w.left.color == RED and w.right.color == BLACK
                w.left.color = BLACK    //case3
                w.color = RED           //case3
                RIGHT_ROTATE(T, w)      //case3
                w = x.p.right           //case3
            else                    //w.right == RED
                w.color = x.p.color     //case4
                x.p.color = BLACK       //case4
                w.right.color = BLACK   //case4
                LEFT_ROTATE(T, x.p)     //case4
                x = T.root              //case4 循环终止
        else (same as then clause with "right" and "left" exchanged)
    x.color = BLACK
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值