红黑树(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操作。
《红黑树(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) - 删除操作》

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

《红黑树(3) - 删除操作》

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

这种情况下需要重新着色,并且:
a. 如果s的父节点是黑色,则做完删除操作后,还需要检测父节点。


《红黑树(3) - 删除操作》

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) - 删除操作》

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

    原文作者:算法小白
    原文地址: https://blog.csdn.net/shltsh/article/details/46835757
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞