zoukankan      html  css  js  c++  java
  • 红黑树(RBTree)

    红黑树性质

    红黑树是一棵二叉搜索树,它在每个结点上增加了一个存储位用来表示结点颜色,可以是Red或者Black。通过对任意一条从根到叶子的简单路径上各个结点颜色的约束,红黑树可以确保没有一条路径会比其它路径长出2倍,因而是近似于平衡的
    红黑树满足下面的性质:

    1. 每个结点要么是红色的,要么是黑色的。
    2. 根结点是黑色的。
    3. 每个叶结点(NIL)是黑色的。
    4. 如果一个结点是红色的,那么它的两个子结点都是黑色的。
    5. 对每个结点,从该结点开始到其所有后代叶结点的简单路径上,包含的黑色结点的数目相同。

    树高

    定义:从某个结点(x)出发(不包含该结点),达到其后代叶节点的简单路径上包含的黑色结点的数目为黑高(black-height),记为(bh(x)),由性质5可知这一定义是明确的。
    引理:一棵有n个内部结点(不包含NIL叶结点)的红黑树的高度至多为(2lg(n+1))
    证明: 先证明以(x)为根的子树中至少包含(2^{bh(x)}-1)个内部结点。这里采用归纳法证明。
    (x)的高度为0时,(x)是叶结点,黑高(bh(x)=0),所以至少包含(2^0-1=0)个内部结点,满足条件。
    (x)的高度大于0时,(x)的两个子结点的黑高要么为(bh(x)),要么为(bh(x)-1),取决于子结点是红色还是黑色,所以以(x)为根的子树至少包含((2^{bh(x)-1}-1)+(2^{bh(x)-1}-1)+1=2^{bh(x)}-1)个内部结点。
    所以以(x)为根的子树中至少包含(2^{bh(x)}-1)个内部结点。
    为了完成引理的证明,设(h)为树的高度,由性质4可知黑高至少为(frac{h}{2}),于是有:

    [nge 2^{frac{h}{2}}-1 ]

    推出

    [hle 2lg(n+1) ]

    即得证

    旋转

    红黑树通过旋转(rotation)来修改指针结构,通过左旋(left-rotation)和右旋(right-rotation)来重新均衡一下左右子树的高度:

    LEFT-ROTATE(T, x)
        // 假设x.right不为NIL,且根节点的父节点为NIL
        y = x.right
        // y的左儿子变为x的右儿子
        x.right = y.left
        if y.left != T.nil
            y.left.p = x
        // y变成x父节点的儿子
        y.p = x.p
        if x.p == T.nil
            T.root = y
        else if x == x.p.left
            x.p.left = y
        else
            x.p.right = y
        // x变为y的左儿子
        y.left = x
        x.p = y
    

    RB-INSERT-FIXUP

    当新插入一个红色结点后有可能违反红黑树的性质,需要调用RB-INSERT-FIXUP函数重新修复红黑树的性质:

    RB-INSERT-FIXUP(T, z)
        while z.p.color == RED
            // 由于根节点是黑色,所以z.p.p一定存在
            if z.p == z.p.p.left
                y = z.p.p.right
                if y.color == RED
                    z.p.color = BLACK                  // case1
                    y.color = BLACK                    // case1
                    z.p.p.color = RED                  // case1
                    z = z.p.p                          // case1
                else
                    if z == z.p.right
                        z = z.p                        // case2
                        LEFT-ROTATE(T, z)              // case2
                    z.p.color = BLACK                  // case3
                    z.p.p.color = RED                  // case3
                    RIGHT-ROTATE(T, z.p.p)             // case3
            else
                same as above with "left" and "right" exchanged
        T.root.color = BLACK
    

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

    情况2:z的叔结点y是黑色的,且z是一个右孩子
    情况3:z的叔结点y是黑色的,且z是一个左孩子

    分析:对于情况1,每次z指针沿着树提升2层,对于情况2和情况3最多执行一次,因为情况2可以转化到情况3,情况3执行完就恢复红黑树的性质了,所以最多发生2次旋转操作。

    删除

    要删除红黑树的一个结点z时,并不是直接删除结点z,而是删除一个用来替代z的结点y,当z没有子结点时,y就是z本身;当z只有一个子结点时,y就是z的子结点;当z有两个子结点时,y就是结点z的后继结点。当结点y被删除后,由结点x来接替y空缺的位置。

    // 将v移植到u的父节点下替代u
    RB-TRANSPLANT(T, u, v)
        if u.p == T.nil
            T.root = v
        else if u == u.p.left
            u.p.left = v
        else
            u.p.right = v
        // 这里v可以为T。nil
        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)
        else if z.right == T.nil
            // 只有左子结点
            x = z.left
            RB-TRANSPLANT(T, z, z.left)
        else
            // 有两个子结点,那么找出后继y
            y = TREE-MINIMUM(z.right)
            y-original-color = y.color
            x = y.right
            if y.p == z
                // x可以为T.nil
                x.p = y
            else
                RB-TRANSPLANT(T, y, y.right)
                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
        if y-original-color == BLACK
            RB-DELETE-FIXUP(T, x)
    

    当删除了一个黑色结点之后,结点x就需要被当作红黑结点,或者双重黑色结点来看待,才能符合黑高一致的性质,下面需要用RB-DELETE-FIXUP函数来进行修复:

    RB-DELETE-FIXUP(T, x)
        while x != T.root and x.color == BLACK
            if x == x.p.left
                // 由于x是双重黑色的结点,所以兄弟结点w不为nil
                w = x.p.right
                if w.color == RED
                    w.color = BLACK                    // case1
                    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
                else
                    if w.right.color == BLACK
                        w.left.color = BLACK           // case3
                        w.color = RED                  // case3
                        RIGHT-ROTATE(T, w)             // case3
                        w = x.p.right                  // case3
                    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 above with "left" and "right" exchanged
        x.color = BLACK
    

    情况1:x的兄弟结点w是红色的
    情况2:x的兄弟结点w是黑色的,且w的两个子结点也是黑色的
    情况3:x的兄弟结点w是黑色的,且w的左孩子是红色的,右孩子是黑色的
    情况4:x的兄弟结点w是黑色的,且w的右孩子是红色的

    下图中,黑色表示黑色结点,深色阴影表示红色结点,浅色阴影(没有阴影)表示既可以为黑色也可以为红色的结点:

    分析::情况1可以转换为情况2,3,4,并且情况1转换之后新的结点x将为红色,会终止循环;只有情况2会继续循环,并且每一次将x指针沿着树提升1层;执行完情况3和情况4之后就恢复红黑树性质了,所以最多执行3次旋转操作。

  • 相关阅读:
    [No000088]并行循环vs普通循环
    [No000087]Linq排序,SortedList排序,二分法排序性能比较
    [No000086]C#foreach集合被改变,报错处理方案
    [No000085]C#反射Demo,通过类名(String)创建类实例,通过方法名(String)调用方法
    [No000084]C# 使用Log4Net(1)-快速建立一个demo
    [No000082]Convert和Parse的区别/Convert.ToInt32()与int.Parse()的区别
    [No000081]SVN学习笔记1-服务端搭建
    [No00007F]2016-面经[下] 英文简历写作技巧
    [No00007E]2016-面经[中]
    [No00007D]2016-面经[上]
  • 原文地址:https://www.cnblogs.com/HachikoT/p/14147072.html
Copyright © 2011-2022 走看看