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

 

详细可參考以下两个链接:
红黑树(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操作。

红黑树(3) - 删除操作_删除节点
上图中,在转换为了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的两个孩子都是红色)。

红黑树(3) - 删除操作_中序遍历_02
d. Right Left Case (s是右孩子,且r是s的左孩子)。

 

 

红黑树(3) - 删除操作_中序遍历_03

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

这样的情况下须要又一次着色。而且:
a. 假设s的父节点是黑色,则做完删除操作后,还须要检測父节点。
红黑树(3) - 删除操作_删除节点_04
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。

 

 

红黑树(3) - 删除操作_父节点_05

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