【算法导论】笔记-第十二章 红黑树

第12章 红黑树

12.1 红黑树的性质

  • 红黑树在每个结点上添加一个存储位来表示结点的颜色,可以是RED或BLACK。

  • 通过对任何一条从根到叶子的简单路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其它路径长出2倍,因而近似于平衡的

  • 树中每个结点包含5个属性: c o l o r , k e y , l e f t , r i g h t , p color, key, left, right, p color,key,left,right,p

  • 红黑树性质:

    • 二叉搜索树

    • 结点或是红色,或是黑色

    • 根结点是黑色结点

    • 每个叶子结点都带有两个空的黑色结点(NIL结点)

    • 如果一个结点是红色,则它的两个子结点都是黑色(或者说从每个叶子到根的所有路径上不能有两个连续的红色节点)

    • 对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

  • 为了便于处理红黑树代码边界条件,使用一个哨兵来代表NIL。哨兵 T . n i l T.nil T.nil与树中普通结点有相同属性。它的 c o l o r color color属性为BLACK,其它属性为任意值。所以我们可以使用一个哨兵 T . n i l T.nil T.nil来代表所有的NIL

  • 黑高:从某一结点出发(不包含该节点)到达一个叶结点的任意一条简单路径上黑色结点个数称为该结点的黑高,记为 b h ( x ) bh(x) bh(x)

  • 一颗有 n n n个内部结点的红黑树的高度至多为 2 lg ⁡ ( n + 1 ) 2\lg(n+1) 2lg(n+1)

  • 红黑树的python实现:

    class ColorEnum(Enum):
        RED = "red"
        BLACK = "black"
     
     
    class RBTreeNode():
        def __init__(self):
            self.value = None
            self.left = None
            self.right = None
            self.p = None
            self.color = None
    

12.2 旋转

  • 指针结构的修改是通过旋转来完成的,这种操作能保持二叉搜索树性质。

  • 左旋:假设在某个结点x上做左旋,它的右孩子为y。左旋则以x到y的链为“支链”进行,它使得y称为该子树新的根结点,x为y的左孩子,y的左孩子成为x的右孩子。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lL1hCiCI-1614663448616)(img/20181031000037863.png)]

  • 伪代码:LEFT-ROTATE(T, x)

    y = x.right
    x.right = y.left
    if y.left != T.nil
        y.left.p = x
    y.p = x.p
    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.p = y
    
  • python代码:

    class RB_Tree(object):
        def __init__(self):
            self.root = None
     
        def left_rotate(self, x: RBTreeNode):
            y = x.right
            x.right = y.left
            y.left.p = x
            y.p = x.p
            if not x.p:
                self.root = y
            elif x == x.p.left:
                x.p.left = y
            else:
                x.p.right = y
            y.left = x
            x.p = y
     
        def right_rotate(self, y: RBTreeNode):
            x = y.left
            y.left = x.right
            x.right.p = y
            y.p = x.p
            if not y.p:
                self.root = x
            elif y == y.p.right:
                y.p.left = x
            else:
            y.p.right = x
            x.right = y
            y.parent = x
    

12.3 插入

  • 插入过程与二叉搜索树差不太多,搜索树中为NULL的地方,现在被替换成nil哨兵节点,其次插入的节点我们总是将其着色为红色。在完成插入之后,为保证红黑性质能继续保持,我们调用一个辅助程序RB-INSERT-FIXUP来对结点重新着色并旋转。

  • 伪代码:

    • 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
      elseif z.key < y.key
          y.left = z
      else y.right = z
      z.left = T.nil
      z.right = T.nil
      z.color = RED
      BR-INSERT-FIXUP(T, z)
      
    • BR-INSERT-FIXUP(T, z)

      while z.p.color == RED
          if z.p == z.p.p.right
              y = z.p.p.left
              if y.color == RED
                  z.p.color = BLACK
                  y.color = BLACK
                  z.p.p.color = RED
                  z = z.p.p
              else if z = z.p.right
                  z = z.p
                  LEFT-ROTATE(T, z)
              z.p.color = BLACK
              z.p.p.color = RED
              RIGHT-ROTATE(T, z, p, p)
          else
              T.root.color = BLACK
      
  • python代码:

    def rb_insert(self, z: RBTreeNode):
        y = None
        x = self.root
        while x:
            y = x
            if z.value < x.value:
                x = x.left
            else:
                x = x.right
        z.p = y
        if not y:
            self.root = z 
        elif z.value < y.value:
            y.left = z
        else:
            y.right = z
        z.left = None
        z.right = None
        z.color = ColorEnum.RED
        self.rb_insert_fixup(z) 
    
    def rb_insert_fixup(self, z):
        while z.p.color == ColorEnum.RED:
            if z.p.p.left == z.p:
                y = z.p.p.right
                if y.color == ColorEnum.RED:
                    z.p.color = ColorEnum.BLACK
                    y.color = ColorEnum.BLACK
                    z.p.p.color = ColorEnum.RED
                    z = z.p.p
                elif y.color == ColorEnum.BLACK and z == z.p.right:
                    z = z.p
                    self.left_rotate(z)
                else:
                    z.p.color = ColorEnum.BLACK
                    z.p.p.color = ColorEnum.RED
                    self.right_rotate(z.p.p)
            else:
                y = z.p.p.left
                if y.color == ColorEnum.RED:
                    z.p.color = ColorEnum.BLACK
                    y.color = ColorEnum.BLACK
                    z.p.p.color = ColorEnum.RED
                    z = z.p.p
                elif y.color == ColorEnum.BLACK and z == z.p.left:
                    z = z.p
                    self.right_rotate(z)
                else:
                    z.p.color = ColorEnum.BLACK
                    z.p.p.color = ColorEnum.RED
                    self.left_rotate(z.p.p)
    
  • 我们之前的插入操作,可能出现问题的就是性质2,4,对于2号性质,如果插入的是一颗空树(只有nil节点),那么根结点就是红的,就不满足2性质。如果插入的节点的父节点是红色,而我们插入的也是一个红色的节点,那么就不满足4性质了。

  • 处理:

    • 性质2:在调整函数最后加上一句root->color=BLACK即可,将根结点染黑。

    • 性质4:我们需要调整的判断条件就是父节点如果为红,就需要继续调整。这里实际上就要分三种情况,首先都已经默认z的父节点z->p的颜色为红色,循环条件为while(z.color==RED):

      • 情况一:z的叔节点y是红色,其中z为要调整的节点(下同):

        ​ 将z的父节点和叔节点都染黑,而将z的祖父节点染红,然后这时我们再将z点指向z的祖父节点位置,然后继续进入循环

      • 情况二:z的叔节点y是黑色的且z是一个右孩子

        ​ 先让z=z.p,然后再以z为支点进行左旋,因为左旋会让z下降一级,所以实际上z还是指向的原来那一层的节点,z.p.p的位置还是没有变。

      • 情况三:z的叔节点y是黑色的且z是一个左孩子

        ​ z.p染黑,z.p.p染红,然后以z.p.p为支点右旋

12.4 删除

  • 步骤:

    • 查找要删除的值所在的节点,如果不存在,删除失败,若存在,执行步骤2
    • 如果要删除的节点不是叶子节点,用要删除节点的后继节点替换(只进行数据替换即可,颜色不变,此时也不需要调整结构),然后删除后继节点。
  • 伪代码:

    • RB-TRANSPLANT(T, u, v)

      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.right
          RB-TRANSPLANT(T, z, z.right)
      else y = TREEE-MINIMUM(z.right)
          y-original-color = y.color
          x = y.right
          if y.p == z
              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)
      
    • RB-DELETE-FIXUP(T, x):恢复红黑树性质

      while x != T.root and x.color == BLACK
          if x == x.p.left
             w = x.p.right    
             if w.color = RED
                 w.color = BLACK
                 x.p.color = RED
                 LEFT-ROTATE(T, x.p)
                 w = x.p.right
             if w.left.color == BLACK and w.right.color == BLACK 
                 w.color = RED				
                 x = x.p
             else if w.right.color == BLACK
                 w.left.color = BLACK
                 w.color = RED 	
                 RIGHT-ROTATE(T, w)
                 w = x.p.right
              w.color = x.p.color
             x.p.color = BLACK
             w.right.color = BLACK
             LEFT_ROTATE(T, x.p)
             x = root[T]
         else
              x.color = BLACK
      
  • 红黑树T中删除结点z可能出现的4种情况:

    • z没有左孩子(即左孩子为T.nil),则用其右孩子来替换z,这个右孩子可以是T.nil,也可以不是,所以这种情况包含了两种情形:z没有孩子、z只有一个右孩子。

    • z有左孩子,没有右孩子,则用其左孩子来替换z。

    • z既有左孩子又有右孩子。我们找z的右子树的最小结点y来替换z,因为这样能继续保证左孩子<父结点<右孩子。这种情况下y可以分为两种情况处理:

      • y是z的右孩子,则直接用y替换z。

      • y不是z的右孩子,则先用y的右孩子替换y,然后再用y替换z。

        这两种情况用y替换z后,y的颜色要变为z的颜色。

  • python代码:

    class RB_Tree(object):    
        def rb_delete(self, z):
            if not z.left or not z.right:
                y = z
            else:
                y = self.find_successor(z)
            if y.left:
                x = y.left
            else:
                x = y.right
            x.p = y.p
            if not y.p:
                self.root = x
            elif y == y.p.left:
                y.p.left = x
            else:
                y.p.right = x
            if y != z:
                z.value = y.value
            if y.color == ColorEnum.BLACK:
                self.rb_delete_fixup(x)
            return y
     
        def rb_delete_fixup(self, x):n:
            while self.root != x and x.color == ColorEnum.BLACK:
                if x == x.p.left:
                    w = x.p.right
                    if w.color == ColorEnum.RED:
                        w.color = ColorEnum.BLACK
                        x.p.color = ColorEnum.RED
                        self.left_rotate(x.p)
                        w = x.p.right
                    if w.left.color == ColorEnum.BLACK and w.right.color == ColorEnum.BLACK:
                        w.color = ColorEnum.RED
                        x = x.p
                    elif w.right.color == ColorEnum.BLACK:
                        w.left.color = ColorEnum.BLACK
                        w.color = ColorEnum.RED
                        self.right_rotate(w)
                        w = x.p.right
                    w.color = x.p.color
                    x.p.color = ColorEnum.BLACK
                    w.right.color = ColorEnum.BLACK
                    self.left_rotate(x.p)
                    self.root = x
                else:
                    w = x.p.left
                    if w.color == ColorEnum.RED:
                        w.color = ColorEnum.BLACK
                        x.parent.color = ColorEnum.RED
                        self.right_rotate(x.p)
                        w = x.p.left
                    if w.right.color == ColorEnum.BLACK and w.left.color == ColorEnum.BLACK:
                        w.color = ColorEnum.RED
                        x = x.p
                    elif w.left.color == ColorEnum.BLACK:
                        w.right.color = ColorEnum.BLACK
                        w.color = ColorEnum.RED
                        self.left_rotate(w)
                        w = x.p.left
                    w.color = x.p.color
                    x.p.color = ColorEnum.BLACK
                    w.left.color = ColorEnum.BLACK
                    self.right_rotate(x.p)
                    self.root = x
            x.color = ColorEnum.BLACK
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

From Star.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值