红黑树(3) - 删除操作

在本系列的前面两篇文章中。已经介绍了红黑树以及其插入操作。

详细可參考以下两个链接:
红黑树(1) - 介绍
红黑树(2) - 插入操作

1.删除操作介绍

类似于插入操作。红黑树进行删除节点时,也使用又一次着色以及旋转这两种方式。来维护它的属性。在插入操作中,我们主要是依靠检測叔节点的颜色来决定哪种场景。在删除操作中,我们使用检測兄弟的颜色,来决定是哪种场景。


在插入操作中,最常见的违反红黑树属性的一种情况是存在两个连续的红色节点。

而在删除操作中。常见的情况是,当删除节点是黑色时,会影响从根节点到叶子的黑色节点高度。违反红黑树的性质5。


删除的过程相对照较复杂。为了便于理解删除过程,我们将使用到"double black"的概念。当一个黑色节点被删除,而且被它的黑色孩子代替时,这个孩子就标记为double black。因此,基本的工作就变为了将这个double black转换为single black。

2.删除操作步骤

以下是具体的删除步骤。

在以下的内容里。d表示被删节点。c表示将用于替换d的孩子节点

运行标准的二叉搜索树的删除操作。在删除过程中,假设d是叶子或者仅仅有一个孩子。则操作比較简单,基本上直接删除就能够了。而对于存在两个孩子的节点,能够先查找到d的中序遍历时的后继节点。用它的值替换掉d的值,然后再删除这个后继节点(中序遍历时的兴许节点总是一个叶子或仅仅有一个孩子)。

例如以下图所看到的。这种话。我们仅仅须要处理被删节点是叶子或仅仅有一个孩子的这种情况。

              50                                            60                                        60
           /     \           delete(50)               /   \          delete(60')           /  \
        40     70    ----------------->      40    70 ------------------>   40   70
                 /  \      后继节点赋值                  /  \      删除后继节点               \
              60   80   给被删节点                  60'   80                                      80 
上图中,被删节点d是50,则它的中序遍历后继节点是60。用后继节点的值替换d的值,然后删除60这个后继节点。
当然,在本步骤中,也能够选取前驱节点(即被删节点左子树最大值)进行替换。

原理与后继节点相似。这里不再描写叙述。

3. 简单场景-d或者c是红色

使用孩子节点c替换d。然后将其置为黑色。这样黑色高度维持不变。这是由于d和c不可能同一时候是红色。当中必然有一个为黑色。
本步骤会覆盖到以下的这4种场景。

              30                            30
           /     \       delete(20)        /   \
          20      40    ------------->    10    40 
         /                                  
        10                          
              30                            30
           /     \       delete(10)        /   \
          10      40    ------------->    20    40 
            \                               
            20         
              30                            30
           /     \       delete(20)        /   \
          20      40    ------------->    10    40 
         /                                  
        10        
              30                            30
           /     \       delete(10)        /   \
          10      40    ------------->    20    40 
            \                                  
            20  
对于d拥有两个孩子(两颗子树)的场景。如以下的两个图所看到的。能够採用第2节提供的方法。转换为处理叶子节点或单个孩子的场景。
              40                                 40                                40
           /     \        delete(20)           /   \        delete(30')           /  \
          20      50 ---------------------->  30    50 --------------------->   30   50
         /  \          运行步骤2.1,将d的       /  \      此时转换为了删除叶子30'.   /
        10  30         后继节点的值赋给d.      10  30'                           10
              40                                 40                                40
           /     \        delete(20)           /   \        delete(30')           /  \
          20      50 ---------------------->  30    50 --------------------->   30   50
         /  \          运行步骤2.1,将d的       /  \      此时转换为了删除叶子30'.   /
        10  30         后继节点的值赋给d.      10  30'                           10

4. 复杂场景-d和c都是黑色(包含d是叶子)

4.1. 孩子节点c是double black

此时,主要工作就是将这个double black转换为single black。注意:当d是叶子时,则默认c为null节点而且为黑色。

所以,假设删除的是黑色叶子,则也会引发double black操作。


上图中,在转换为了double black后,实际上已经变成了4.2.1.c的场景。能够使用Right Right Case旋转方式。
终于,删除节点20后,这棵树调整为:
              30                            40
           /     \       delete(20)        /   \
          20      40    ------------->    30    50 
                    \                             
                     50               

4.2. d是double black。或者d不是根节点

在这样的情况下,如果s表示d的兄弟节点,则存在以下这些场景。

4.2.1. s是黑色,而且s的孩子中至少有一个是红色。则进行旋转

如果s的这个红色的孩子为r,则依据s和r的位置,能够分为4种情况。

a. Left Left Case (s是左孩子,且r是s的左孩子或者s的两个孩子都是红色)。

这样的情形与以下的Right Right Case正好相反。

b. Left Right Case (s是左孩子,且r是s的右孩子)。这样的情形与以下的Right Left Case正好相反。

c. Right Right Case (s是右孩子,且r是s的右孩子或者s的两个孩子都是红色)。


d. Right Left Case (s是右孩子,且r是s的左孩子)。



4.2.2. s是黑色,而且s的两个孩子都是黑色(包含s是叶子)

这样的情况下须要又一次着色。而且:
a. 假设s的父节点是黑色,则做完删除操作后,还须要检測父节点。


b. 假设s的父节点是红色,则不须要再检測父节点,而是能够简单地将其设置为黑色(红色+double black = single black)。

4.2.3. s是红色,运行旋转操作,提升s。而且又一次着色s以及它的父节点

此时新的兄弟节点总是黑色的(下图的节点25)。至此,已经将这棵树通过旋转,转换为了兄弟为黑色的这样的场景。使用4.2.1或者4.2.2继续处理。这样的情形能够分为两种情况。
a. Left Case (s是左孩子)。右旋转父节点p。
b. Right Case (s是右孩子). 左旋转父节点p。



4.3. 假设c是根节点,将其转换为single black然后返回(全然二叉树的黑色高度减1).

posted @ 2016-04-17 19:14  zfyouxi  阅读(304)  评论(0编辑  收藏  举报