• 红黑树研究记录理论


    一、     什么是红黑树?

    红黑树,是二叉查找树的一种,相对于AVL树,通过放弃绝对的平衡而提高了插入的速度,它可以不要求节点的左右子树高度差小于2,但保证一颗子树的高度绝对小于另一颗子树高度的2倍。

    一棵拥有n个内部结点的红黑树的树高h<=2log(n+1) 。

    红黑树性质:

    1. 每个结点或红或黑。

    2. 根结点为黑色。

    3. 每个叶结点(实际上就是NULL指针)都是黑色的。

    4. 如果一个结点是红色的,那么它的周边3个节点都是黑色的。(也就是说两个红色节点不可能相连)

    5. 对于每个结点,从该结点到其所有子孙叶结点的路径中所包含的黑色结点个数都一样。

    二、     左旋和右旋

    红黑树中,最基础也是最重要的就是左旋和右旋了,很好理解,就是以某个节点为中心,对它的父节点及子节点进行旋转。

    1.左旋:

      

    图:左旋(以C为中心)

    说明:以C为中心左旋,C的父节点A将变为C的左子节点,如果C的左子结点有值,则变为C的原父节点A的右子结点。

    2.右旋:

    图:右旋(以B为中心)

    说明:以B为中心右旋,B的父节点A将变为B的右子节点,如果B的右子节点有值,则变为B的原父节点A的右子结点。

    三、     添加节点(Insert)(图中所有的NL节点没有画出来)

    伪代码:

     1 RB-INSERT(T, z)
     2     y = NIL
     3     x = ROOT[T]
     4     while x != NIL
     5         y = x
     6         if key[x] > key[z]
     7             x = left[x]
     8         else
     9             x = right[x]
    10         p[z] = y
    11     if y == NIL
    12         ROOT[T] = z
    13     else
    14         if key[y] > key[z]
    15             left[y] = z
    16         else
    17             right[y] = z
    18     left[z] = NIL
    19     right[z] = NIL
    20     color[z] = RED
    21     RB-INSERT_FIXUP(T, z)
    22 
    23 RB-INSERT-FIXUP(T, z)
    24     while color[p[z]] == RED
    25         if p[z] == left[p[p[z]]]
    26             y = right[p[p[z]]]
    27             //case 1
    28             if color[y] == RED
    29                 color[p[z]] = BLACK
    30                 color[y] = BLACK
    31                 color[p[p[z]]] = RED
    32                 z = p[p[z]]
    33             else
    34                 //case 2
    35                 if z == right[p[z]]
    36                     LEFT-ROTATE(T, z)
    37                     z = left[z]
    38                 //case 3
    39                 color[p[z]] = BLACK
    40                 color[p[p[z]]] = RED
    41                 RIGHT-ROTATE(T, p[z])
    42         else 
    43             y = left[p[p[z]]]
    44             //case 1
    45             if color[y] == RED
    46                 color[p[z]] = BLACK
    47                 color[y] = BLACK
    48                 color[p[p[z]]] = RED
    49                 z = p[p[z]]
    50             else
    51                 //case 2
    52                 if z == right[p[z]]
    53                     RIGHT-ROTATE(T, z)
    54                     z = right[z]
    55                 //case 3
    56                 color[p[z]] = BLACK
    57                 color[p[p[z]]] = RED
    58                 LEFT-ROTATE(T, p[z])
    59                 
    60     color[ROOT[T]] = BLACK

    总结步骤:

    ①  插入节点后,当前节点指向插入节点,跳到②

    ②  如果当前节点为根节点,则将节点颜色变为黑,跳到(结束),否则跳到③

    ③  如果当前节点的父节点为黑色,无需进一步处理,跳到(结束),否则跳到④

    ④  当前节点的父节点为红色,如果父节点为祖节点的左子节点,则跳到⑤,否则跳到⑧

    ⑤  case 1:如果叔节点为红色,则父节点和叔节点变黑,祖节点变红,当前节点指向祖节点,跳到②,否则跳到⑥

    ⑥  case 2:叔节点为黑色,如果当前节点为父节点的右子节点,形成(LR),则沿当前节点进行左旋,当前节点指向当前节点的左子节点,跳到⑦

    ⑦  case 3:当前节点的父节点变黑,祖节点变红,沿当前节点的父节点进行右旋,跳到②

    ⑧  当前节点的父节点为为祖节点的右子节点,则进行⑤-⑦的相反操作

    四、     删除节点(Delete)

    伪代码:

    TREE_SUCCESSOR(z)
        v = right[z]
        while left[v] != NIL
            v = left[v]
        return v
    
    RB-DELETE(T, z)
        if left[z] == NIL and right[z] == NIL
            x = z
            y = z
        else if left[z] == NIL or right[z] == NIL
            y = z
        else
            y = TREE_SUCCESSOR(z)
        if left[y] != NIL
            x = left[y]
        else if right[y] != NIL
            x = right[y]
        else
            x = y
        if x != y            //delete y from the tree
            p[x] = p[y]
            
            if p[y] == NIL
                ROOT[T] = x
            else if y == left[p[y]]
                left[p[y]] = x
            else
                right[p[y]] = x
        if y != z
            key[z] = key[y]
        if color[y] == BLACK
            RB-DELETE_FIXUP(T, x)
        if x == y
        if y == left[p[y]]
                left[p[y]] = NIL
            else
                right[p[y]] = NIL
            if p[y] == NIL
            ROOT[T] = NIL
        return y    
    RB
    -DELETE-FIXUP(T, x) while x!= root[T] and color[x] == BLACK if x = left[p[x]] //x is Left w = right[p[x]] //case 1:w is RED if color[w] = RED color[w] = BLACK color[p[x]] = RED LEFT-ROTATE(T, w) w = right[p[x]] //w is BLACK //case 2: right[w] and left[w] is BLACK if color[left[w]] == BLACK and color[right[w]] == BLACK color[w] = RED x = p[x] else //case 3: right[w] is BLACK and left[w] is RED if color[left[w]] == RED color[left[w]] = BLACK color[w] = RED RIGHT-ROTATE(T, left[w]) w = right[p[x]] //case 4:right[w] is RED color[w] = color[p[x]] color[p[x]] = BLACK color[right[w]] = BLACK LEFT-ROTATE(T, w) x = ROOT[T] else //x is Right w = left[p[x]] if color[w] = RED //w is RED color[w] = BLACK color[p[x]] = RED RIGHT-ROTATE(T, w) w = left[p[x]] //w is BLACK if color[right[w]] == BLACK and color[left[w]] == BLACK color[w] = RED x = p[x] else if color[left[w]] == BLACK color[left[w]] = BLACK color[w] = RED LEFT-ROTATE(T, right[w]) w = left[p[x]] color[w] = color[p[x]] color[p[x]] = BLACK color[left[w]] = BLACK RIGHT-ROTATE(T, w) x = ROOT[T] color[x] = BLACK

    说明:上面RB-DELETE函数中有部分是红色代码,这部分代码与算法导论中的代码有所区别,主要是针对删除的节点时叶子节点的情况,虽然原有代码也能完成功能,但会增加后面RB-DELETE-FIXUP的操作难度。

    总结步骤:

    删除节点的操作稍微复杂一些,但还是有一定规则的,主要分成两个步骤:删除节点和修复性质。

    删除:

    ①  如果要删除的节点至少有一个子节点为NIL,则将当前节点指向待删除节点,跳到③,否则跳到②

    ②  待删除节点有两个子节点,则将当前节点指向待删除节点右子树的最左节点,也就是当前节点在中序遍历时的后继节点,将当前节点的值覆盖待删除节点的值,跳到③

    ③  如果当前节点的左子节点不为NIL,则将当前节点的左子节点作为X,否则将当前节点的右子节点作为X,删除当前节点,包括当前节点的父节点、左右子节点、父节点的子节点和子节点的父节点的指针的操作,将当前节点指向X,如果当前节点的颜色为红色,则结束,否则跳到④

    平衡修复:

    ④  如果当前节点为黑色,且当前节点不为根节点,则跳到⑤,否则结束

    ⑤  如果当前节点为父节点的左子节点,则跳到⑥,否则跳到⑩

    ⑥  case1:当前节点为父节点的左子节点,如果它的兄弟节点W为红色,则使其兄弟节点变黑色,父节点变红色,沿它的兄弟节点W左旋,将W指向当前节点的新兄弟节点,跳到⑦;

    ⑦  case2:如果W的左子节点和右子节点都为黑色,则将W变红,当前节点指向其父节点,跳到④,否则跳到⑧

    ⑧  case3:如果W的右子节点为黑色(W的左子节点为红色),则将W的左子节点变黑,W变红,沿W的左子节点右旋,W指向当前节点的新兄弟节点,跳到⑨

    ⑨  case4:W的颜色变为当前节点的父节点的颜色,当前节点的父节点变为黑色,W的右子节点变为黑色,沿W进行左旋,当前节点指向根节点,跳到④

    ⑩  如果当前节点为父节点的右子节点,则完成⑥-⑨的相反操作

     

     

  • 相关阅读:
    [vue Debugger] sourcemap
    [vuex]持久化存储
    [vuex]字典值封装到vuex缓存
    [vue]常用指令集合
    VSCode插件集合
    [element-ui] 表格点击出现编辑效果实现
    [pdf] 插件实现pdf上传预览、打印
    [html] 特殊字符
    [element-ui] 穿梭框对象重复不添加方案
    Redis持久化
  • 原文地址:https://www.cnblogs.com/geekma/p/2566204.html
Copyright © 2020-2023  润新知