必须要把红黑树讲清楚,看完还不明白请直接找我之2(共四篇)—红黑树的插入

  红黑树的插入的时间复杂度是O(lgn),插入操作和普通的二叉搜索树几乎一模一样,不同点是在插入后需要看看红黑树的五大性质是否被破坏了,如果被破坏了,那就需要调整了。下面先来看看红黑树插入操作的代码,其实插入操作很简单,假如要插入的节点叫做x,无非就是找到从根节点出发,发现当前节点比x大的走左边路线,发现当前节点比x小的走右边路线,一直走到叶子节点,然后插入这个x节点。关键点是插入后的调整。下面先看看红黑树的插入伪代码(抄自算法导论)

RB-INSERT(T, x)
  y = T.NIL;
  z = T.root;
  // 一直循环直到找到z的合适位置
  while z != T.NIL
    y = z;
    if x.key < z.key
       z = z.left;
    else z = z.right;
  x.p = y;
  if y == T.NIL
     T.root = x;
  elseif x.key < y.key
     y.left = x;
  else y.right = x;
  x.left = T.NIL;
  x.right = T.NIL;
  x.color = RED;
  // 调整恢复红黑树的五大性质
  RB-INSERT-FIXUP(T, x);
  注意到上面我们插入的节点染为红色,为什么不是黑色呢?因为如果黑色的话,调整起来会复杂一些,具体我们后面看删除操作就明白了。下面我们分析一下插入红色节点x后整棵红黑树的变化。首先性质1、3、5肯定是没有变化的。仅可能破坏性质2(x为根节点)和性质4(父节点是红色的)。性质2破坏很好解决,直接染黑就是了。我们重点来讨论性质4被破坏后,如何调整和恢复。这里先说一个指导思想,1.调整恢复过程中千万不能破坏其他性质,否则只会带来更多难以解决的问题,2.指针指向的是需要调整的节点,只要我们能把指针指向的节点的父节点调整为黑色,并且没有破坏性质5,那么问题就解决了。那么总结一下,插入后的所有情况值可能有如下五种

  case 0.插入节点z为根节点——这种情况好办,直接染黑完事,我们不讨论

  case 1.父节点为黑色节点——这种情况最好办,根本没有破坏红黑树的五大性质,不需要做任何调整,我们不讨论

  case 2.父节点为红色节点,叔节点也为红色

  case 3.父节点为红色节点,叔节点为黑色节点,当前节点为右孩子

  case 4.父节点为红色节点,叔节点为黑色节点,当前节点为左孩子

就以上五种情况已经列举了所有可能,我们不关心兄弟节点的颜色,为什么呢?这里可留给大家思考一下,稍微想下就明白了。下面用一个图展示所有需要讨论的情况:


  

  

 下面我们一个情况一个情况地来分析。

  先看case 2:


 可以看到 父节点和叔节点都是红色,再想想我们的指导思想2,要把父节点染黑,那就染黑节点p,这样子的话z子树的黑高就加1了,那只有把节点a染红,同时不能让w子树黑高减一,那就把w染黑,这样z的父节点就成功染黑了,修复了性质4,而且没有破坏性质5。那么上图的红黑树的子树性质就恢复了。此时指针指向节点a,再看看a往上的子树的五大性质是否有保持,此时情况可能转case1,2,3,4,如果转了case1,那么性质就恢复了。看看伪代码,伪代码如下:

// case 2伪代码
// 叔节点
y == x.p.p.right;
if y.color == red
  x.p.color = black;
  y.color = black;
  x.p.p.color = red;
  x = x.p.p;


接着来看case 3:

 

 这个case 3比较有趣,父节点为红色,叔节点为黑色,当前节点为右孩子。一看两个红节点,怎么旋转都不会破坏红黑树原有的性质,而且旋转一下,把当前节点指向节点p的话,情况就变得和case 4一模一样了。变为了父节点(z)红色,叔节点为黑色,当前节点为左孩子。这样正好我们把case 3和case 4合并起来了,只要解决case 4即可。看看case 3伪代码,只需两行:

// case 3
x = x.p;
LEFT-ROTATE(T, x)


那么接下来看看case 4


case 4中我们一步步的分析如何从左边的图调整为右边的图,首先还是回到我们的指导思想,把x指针指向节点的父节点染黑,染黑后发现改变了子树Q和W的黑高,那么一个做法就是右旋转a节点,右旋节点a后发现子树F和G的黑高加了1,破坏了性质5,那么把节点a染红,正好就把黑高调整回来了,经过这样的调整,也就变成了上面右边的红黑树图案了。至此,性质4恢复了,红黑树的插入调整也正常结束。case 4伪代码:

// case 4
x.p.color = black;
x.p.p.color = red;
RIGHT-ROTATE(T, x.p.p);
至此红黑树的插入操作分析完毕。下面整理一段整个插入流程的伪代码,以方便大家全局观看插入操作:

// 红黑树插入逻辑
RB-INSERT-FIXUP(T, x)
while x.p.color = red
  if x.p == x.p.p.left
    y == x.p.p.right;
    if y.color == red
      // case 2
      x.p.color = black;
      y.color = black;
      x.p.p.color = red;
      x = x.p.p;
    else if  x == x.p.right
      // case 3
      x = x.p;
      LEFT-ROTATE(T, x)
    // case 4
    x.p.color = black;
    x.p.p.color = red;
    RIGHT-ROTATE(T, x.p.p);
  else(x.p为右子树,和左子树操作相反即可);
T.root.color = black;





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章