红黑树——原理

1.性质

  红黑树是一种二叉查找树,但是每个节点增加一个表示结点颜色(红或黑)的字段,并且满足一下条件:

  • 每个节点或是红的,或是黑的
  • 根节点是黑的
  • 每个叶结点(NIL)是黑的
  • 如果一个节点是红的,则它的两个儿子都是黑的
  • 对每个节点,从该结点到其子孙节点的所有路径上包含相同数目的黑节点

  为方便处理边界条件,我们采用一个哨兵来表示NIL,如果某节点没有一个子结点或父结点,则该结点的相应指针指向NIL。

  一棵有n个内结点(不包括NIL)的红黑树高度之多为2lg(n+1)。

 

2.旋转

  在红黑树上执行插入或删除操作时,结果都可能违反红黑树的性质,我们需要改变树的指针结构来保持红黑树的性质。旋转是修正过程中会用到的操作,分为左旋和右旋,这里只介绍左旋操作,右旋刚好与左旋操作相反。下面是旋转的示意图

  

LEFT-ROTATE(T, x)
    y ← right[x]
    right[x] ← left[x]

    if left[y] != nil[T]
        p[left[y]] ← x

    p[y] ← p[x]

    if p[x] = nil[T]
        root[T] ← y
    else if x = left[p[x]]
        left[p[x]] ← y
    else right[p[x]] ← y
    
    left[y] ← x
    p[x] ← y

 

3.插入

  插入操作与普通二叉树的插入操作接近,将新插入节点设置为红色(尽可能少违反二叉树性质),并在最后增加RB-INSERT-FIXUP(T, z)调节二叉树使其满足红黑树的性质。

RB-INSERT
    y ← nil[T]
    x ← root[T]
    while x != nil[T]
        do y ← x
        if key[z] < key[x]
            x ← left[x]
        else x ← right[x]
    p[z] ← y
    if y = nil[T]
        root[T] = z
    else if key[z] < key[y]
        left[y] ← z
    else
        right[y] ← z
    left[z] ← nil[T]
    right[z] ← nil[T]
    color[z] ← RED
    RB-INSERT-FIXUP(T, z)

  RB-INSERT-FIXUP对插入结果进行修饰,可能会存在以下3中违反红黑树性质的情况(示例图假设“当前节点”为x,且父节点是祖父节点的左孩子,如果是右孩子,则将下面左右调转即可):

  case 1:父亲是红色的,叔叔也是红色的

    处理方法
    1.将“父节点”设为黑色
    2.将“叔叔节点”设为黑色
    3.将“祖父节点”设为红色
    4.将“祖父节点”设为当前节点

      

  case 2:父亲是红色的,叔叔是黑色的,当前节点是右孩子

    处理方法:
    1.将“父节点”作为“当前节点”

    2.以“当前节点”为支点进行左旋

  

  case 3:父亲是红色的,叔叔是黑色的,当前节点是左孩

    处理方法:
    1.将“父节点”设为黑色。
    2.将“祖父节点”设为红色。
    3.以“祖父节点”为支点进行右旋。

  

  以下是RB-INSERT-FIXUP的过程:

RB-INSERT-FIXUP(T, z)
  while color[p[z]] = RED            // 若“当前节点(z)的父节点是红色”,则进行以下处理。
    do 
      if p[z] = left[p[p[z]]]        // 若“z的父节点”是“z的祖父节点的左孩子”,则进行以下处理。
        y ← right[p[p[z]]]        // 将y设置为“z的叔叔节点(z的祖父节点的右孩子)”
        if color[y] = RED              // Case 1条件:叔叔是红色
          color[p[z]] ← BLACK         ▹ Case 1   //  (01) 将“父节点”设为黑色。
              color[y] ← BLACK            ▹ Case 1   //  (02) 将“叔叔节点”设为黑色。
              color[p[p[z]]] ← RED        ▹ Case 1   //  (03) 将“祖父节点”设为“红色”。
              z ← p[p[z]]                 ▹ Case 1   //  (04) 将“祖父节点”设为“当前节点”(红色节点)
        else if z = right[p[z]]        // Case 2条件:叔叔是黑色,且当前节点是右孩子
              z ← p[z]                    ▹ Case 2   //  (01) 将“父节点”作为“新的当前节点”。
              LEFT-ROTATE(T, z)           ▹ Case 2   //  (02) 以“新的当前节点”为支点进行左旋。
        else                  // Case 3条件:叔叔是黑色,且当前节点是左孩子。
              color[p[z]] ← BLACK         ▹ Case 3   //(01) 将“父节点”设为“黑色”。
          color[p[p[z]]] ← RED        ▹ Case 3   //  (02) 将“祖父节点”设为“红色”。
          RIGHT-ROTATE(T, p[p[z]])    ▹ Case 3   //  (03) 以“祖父节点”为支点进行右旋。
      
// 若“z的父节点”是“z的祖父节点的右孩子”,将上面的操作中“right”和“left”交换位置,然后依次执行。       else (same as then clause with "right" and "left" exchanged)   color[root[T]] ← BLACK

 

4.删除

  与插入相似,删除操作也是基本与二叉查找树的删除操作相同,只是在最后要对树做RB-DELETE-FIXUP修复,使得树依然能满足红黑树的性质。

  第一步:将红黑树当作二叉查找树,将节点删除,分三种情况
  1.被删除节点没有儿子,直接将该节点删除
  2.被删除节点只有一个儿子,直接删除该节点,并用该节点的唯一子节点顶替它的位置
  3.被删除节点有两个儿子,先找出它的后继节点;然后把“它的后继节点的内容”与“该节点的内容”交换;之后,删除“它的后继节点”。

  第二步:对树做RB-DELETE-FIXUP操作,使得树满足红黑树的性质。RB-DELETE-FIXUP操作与RB-INSERT-FIXUP操作类似,不再做解释,以下是删除的过程:

RB-DELETE(T, z)
  if left[z] = nil[T] or right[z] = nil[T]         
    y ← z                 // 若“z的左孩子” 或 “z的右孩子”为空,则将“z”赋值给 “y”;
  else y ← TREE-SUCCESSOR(z)      // 否则,将“z的后继节点”赋值给 “y”。
  if left[y] ≠ nil[T]
       x ← left[y]             // 若“y的左孩子” 不为空,则将“y的左孩子” 赋值给 “x”;
  else 
    x ← right[y]             // 否则,“y的右孩子” 赋值给 “x”。   p[x] ← p[y] // 将“y的父节点” 设置为 “x的父节点”   if p[y] = nil[T]   root[T] ← x // 情况1:若“y的父节点” 为空,则设置“x” 为 “根节点”。   else if y = left[p[y]]     left[p[y]] ← x // 情况2:若“y是它父节点的左孩子”,则设置“x” 为 “y的父节点的左孩子”   else right[p[y]] ← x // 情况3:若“y是它父节点的右孩子”,则设置“x” 为 “y的父节点的右孩子”   if y ≠ z   key[z] ← key[y] // 若“y的值” 赋值给 “z”。注意:这里只拷贝z的值给y,而没有拷贝z的颜色     copy y's satellite data into z   if color[y] = BLACK     then RB-DELETE-FIXUP(T, x) // 若“y为黑节点”,则调用   return y
RB-DELETE-FIXUP(T, x)
  while x ≠ root[T] and color[x] = BLACK  
    do 
      if x = left[p[x]]      
        w ← right[p[x]]      // 若 “x”是“它父节点的左孩子”,则设置 “w”为“x的叔叔”(即x为它父节点的右孩子) 
      if color[w] = RED           // Case 1: x是“黑+黑”节点,x的兄弟节点是红色。(此时x的父节点和x的兄弟节点的子节点都是黑节点)。
        color[w] ← BLACK         ▹  Case 1   //   (01) 将x的兄弟节点设为“黑色”。
        color[p[x]] ← RED        ▹  Case 1   //   (02) 将x的父节点设为“红色”。
        LEFT-ROTATE(T, p[x])     ▹  Case 1   //   (03) 对x的父节点进行左旋。
        w ← right[p[x]]          ▹  Case 1   //   (04) 左旋后,重新设置x的兄弟节点。
      if color[left[w]] = BLACK and color[right[w]] = BLACK       
                       // Case 2: x是“黑+黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。         then color[w] ← RED ▹ Case 2 // (01) 将x的兄弟节点设为“红色”。         x ← p[x] ▹ Case 2 // (02) 设置“x的父节点”为“新的x节点”。       else if color[right[w]] = BLACK
                           // Case 3: x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。         then color[left[w]] ← BLACK ▹ Case 3 // (01) 将x兄弟节点的左孩子设为“黑色”。         color[w] ← RED ▹ Case 3 // (02) 将x兄弟节点设为“红色”。         RIGHT-ROTATE(T, w) ▹ Case 3 // (03) 对x的兄弟节点进行右旋。         w ← right[p[x]] ▹ Case 3 // (04) 右旋后,重新设置x的兄弟节点。       else                // Case 4: x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的。         color[w] ← color[p[x]] ▹ Case 4 // (01) 将x父节点颜色 赋值给 x的兄弟节点。         color[p[x]] ← BLACK ▹ Case 4 // (02) 将x父节点设为“黑色”。         color[right[w]] ← BLACK ▹ Case 4 // (03) 将x兄弟节点的右子节设为“黑色”。         LEFT-ROTATE(T, p[x]) ▹ Case 4 // (04) 对x的父节点进行左旋。         x ← root[T] ▹ Case 4 // (05) 设置“x”为“根节点”。     // 若 “x”是“它父节点的右孩子”,将上面的操作中“right”和“left”交换位置,然后依次执行。     else (same as then clause with "right" and "left" exchanged)     color[x] ← BLACK

 

posted @ 2015-02-01 20:17  一同  阅读(1563)  评论(0编辑  收藏  举报