红黑树的理解说明(删除)

来源:互联网 发布:java 策略模式 编辑:程序博客网 时间:2024/05/16 12:39

红黑树5个性质

1、根节点是黑色的

2、每个节点不是黑色的就是红色的

3、叶子节点都是黑色的

4、如果一个节点是红色的,那么他的2个儿子都是黑色的

5、每个节点到叶子节点的每一条路径都包含相同数量的黑节


删除伪代码,伪代码来自:http://blog.csdn.net/v_july_v/article/details/6105630

  1.  1 if left[z] = nil[T] or right[z] = nil[T]    
  2.  2    then y ← z    
  3.  3    else y ← TREE-SUCCESSOR(z)    
  4.  4 if left[y] ≠ nil[T]    
  5.  5    then x ← left[y]    
  6.  6    else x ← right[y]    
  7.  7 p[x] ← p[y]    
  8.  8 if p[y] = nil[T]    
  9.  9    then root[T] ← x    
  10. 10    else if y = left[p[y]]    
  11. 11            then left[p[y]] ← x    
  12. 12            else right[p[y]] ← x    
  13. 13 if y ≠ z    
  14. 14    then key[z] ← key[y]    
  15. 15         copy y's satellite data into z    
  16. 16 if color[y] = BLACK    
  17. 17    then RB-DELETE-FIXUP(T, x)    
  18. 18 return y 


这里要注意这个nil[T]是表示叶节点,与NULL不同,这是个实实在在的对象,表示叶子节点(黑色)


记住:y是要被删除的节点,y可能为z或者z的后继, x是y的儿子节点,x可能等于nil[T](非NULL),当y被删除后,x会替换到y的位置,这点对理解代码很重要


y被从树上断开(删除)后, 只有在y节点是黑色的时候,红黑树的属性:路径黑节点数相同,会发生变化:

        if color[y] = BLACK

          RB-DELETE-FIXUP(T, x)  


y节点是红色的时候,删除y,红黑树的性质不会发生变化。


    RB-DELETE-FIXUP(T, x) 的伪代码:

  1.  1 while x ≠ root[T] and color[x] = BLACK    
  2.  2     do if x = left[p[x]]    
  3.  3           then w ← right[p[x]]    
  4.  4                if color[w] = RED    
  5.  5                   then color[w] ← BLACK                        ▹  Case 1    
  6.  6                        color[p[x]] ← RED                       ▹  Case 1    
  7.  7                        LEFT-ROTATE(T, p[x])                    ▹  Case 1    
  8.  8                        w ← right[p[x]]                         ▹  Case 1    
  9.  9                if color[left[w]] = BLACK and color[right[w]] = BLACK    
  10. 10                   then color[w] ← RED                          ▹  Case 2    
  11. 11                        x ← p[x]                                ▹  Case 2    
  12. 12                   else if color[right[w]] = BLACK    
  13. 13                           then color[left[w]] ← BLACK          ▹  Case 3    
  14. 14                                color[w] ← RED                  ▹  Case 3    
  15. 15                                RIGHT-ROTATE(T, w)              ▹  Case 3    
  16. 16                                w ← right[p[x]]                 ▹  Case 3    
  17. 17                         color[w] ← color[p[x]]                 ▹  Case 4    
  18. 18                         color[p[x]] ← BLACK                    ▹  Case 4    
  19. 19                         color[right[w]] ← BLACK                ▹  Case 4    
  20. 20                         LEFT-ROTATE(T, p[x])                   ▹  Case 4    
  21. 21                         x ← root[T]                            ▹  Case 4    
  22. 22        else (same as then clause with "right" and "left" exchanged)    
  23. 23 color[x] ← BLACK    

进入正题,上面这段代码很简练,意思就是:

一、x是红色,比较容易办,由于删除了一个y是黑色,所以直接将x涂成黑色,即可保持红黑树的性质,只执行第23行即可

二、x是黑色,x是根节点,显然什么也不用干,这颗树还是红黑树,代码只执行第23行

三、x是黑色,x不是根节点,才执行do操作


进入case前,先理一下删除节点的代码,本来x=y.child,y被删除,x就替换到y的位置,那么原来通往x的路径上就少了一个黑节点,我们就想办法在这条路径上增加一个节点并涂黑色(通过旋转实现,旋转可以在某条路径上增加一个节点),或者在其他的路径(兄弟节点路径)上也修改一个节点的颜色(黑->红),来达到红黑树的路径黑节点数目相同的性质,原理是这样,为达目标也不是这么简单,具体通过以下case来实现


先引用一张靓图,出自:http://blog.csdn.net/v_JULY_v/article/details/6109153



case1:x是黑色,x的兄弟w是红色

操作:

  1. color[w] ← BLACK                        ▹  Case 1  
  2. color[p[x]] ← RED                       ▹  Case 1  
  3. LEFT-ROTATE(T, p[x])                    ▹  Case 1  
  4. w ← right[p[x]]                         ▹  Case 1  

说明:

由于在x处原来有个黑的y被删除,导致从B->A就少一个黑节点,再看兄弟节点D点是红色,B一定是黑色,所以没法直接通过改变颜色来维持红黑树的性质了,必须通过旋转来改变路径上的节点数才可能维持红黑性质,怎么旋呢?旋转x的父节点,而且往删除节点的方向去旋转,这样旋转后在删除的那个方向的路径上会多一个节点,然后将多出的节点着一个黑色,就可以使这个路径上的黑节点数平衡,但是可能在旋转点的另一路径上会破坏平衡,所以需要在调整节点后继续循环处理。

1、先考虑直接左旋转B点,旋转后B->C黑节点数未变,但是D->E路径黑节点数会少1,所以不行,而且D节点为红色,旋转后可能会与D的父节点颜色冲突。

2、所以先将D变黑,再旋转,旋转后就不会导致与性质4冲突,D变黑,D节点(黑色)与其父节点的颜色就不会冲突,而且旋转后从D到X节点的黑节点数也恢复到删除前的数量了,但是,仔细看看D->C的路径上黑节点数,多了一个,所以还需要继续往下处理。

3、为了维持性质5,接下来要么将B涂红,要么将C涂红,能将C涂红吗?目测好像可以,但是改变C的颜色后又要判断C的儿子节点是不是红的,如此下去,过于麻烦了。

4、所以将B涂红,将B涂红,与C的颜色不冲突。而且这样会将树转换为了case2或者case3或者case4的状态了,将B涂红后,D到X节点路径的黑节点又回到了删除状态时的样子(即少一个黑节点数),故继续进入其他case。

5、w ← right[p[x]] 加上这句就可以不用循环就直接其他case(因为w是黑的),去掉这句然代码多循环一次,进入其他case也行。

综上说明,case1操作后仅仅是在删除节点的路径上增加了一个红节点(意味着在这条路径上图黑一个节点就可以达到黑节点数平衡),并未恢复红黑树的性质5,黑节点数和未调整时时一样的(比删除前原树小1),经过这次调整后树状态转换为case2或者case3或者case4。


case2,x是黑色,x的兄弟w是黑色,并且兄弟的2子均为黑色

显然,将x的兄弟的黑图成红,即可在兄弟路径上的黑节点数减少一个,达到与x路径的黑节点数一致(由于删除的y是黑色),但是这个调整可能破环性质4(x的父是红色时),故修改x为x的父,再次进入循环,进入循环后,如果x原来是红色,则执行23行,将x图黑(+1黑),即恢复了所有红黑性质,退出;如果x为黑色,需要继续处理直到将黑节点数的性质恢复,这个时候可能进入任何一个case。


case3,x是黑色,x的兄弟w是黑色,并且兄的右子为黑色(左子一定是红色)

先来直接图色看能不能恢复黑节点数性质,显然需要将D图红,D的左子C必须图黑,那么B->E黑节点数少一个将与X路径上一致,但是B->C路径的黑节点数未变化,故以D节点来一次右旋,由于此时C为黑色,D为红色,所有在在B的右孩子的所有路径上的所有黑节点数与进行case操作前是一致的,这样做的好处就是在B的右边孩子路径上会多一个节点,为case4的左旋(为B的左孩子多一个节点)打下了基础;仔细观察,这次操作后,树状图转换为了case4。


case4,x是黑色,x的兄弟w是黑色,并且兄的右子为红色(左子颜色不定)

这个直接进行一次如下操作

  1. color[w] ← color[p[x]]                 ▹  Case 4    
  2. color[p[x]] ← BLACK                    ▹  Case 4    
  3. color[right[w]] ← BLACK                ▹  Case 4    
  4. LEFT-ROTATE(T, p[x])                   ▹  Case 4 
所有红黑性质都修复,设置x ← root[T],退出循环

D->A黑节点数多一,和删除y以前的数量一致

D->C,D->E的黑节点数未变化


总结:删除的修复主要是在删除节点的路径上通过旋转来增加一个节点,然后再在这条路径上图黑一个节点,就修复了由于删除一个黑节点而引起的黑节点数不平衡的性质破环。







0 0
原创粉丝点击