红黑树删除操作

若被删除的结点有两个非叶子结点,那么可以转换为删除一个“替代点”的问题,该替代点最多只有一个非叶子孩子结点。可以通过前驱或者后继(都最多有一个非叶子孩子结点)来替代最初要被删除的结点,所以下面只关注只有一个非叶子孩子结点的问题,一旦我们解决了这个问题,那么解决方法将同样适用于两种情形:
1、原本想删除的结点最多有一个非叶子孩子结点
2、原本想删除的结点有两个非叶子孩子结点(通过前驱和后继可以转化为最多有一个非叶子孩子结点)

好了,现在用N来表示替代结点(要是被删除的结点没有替代结点,那么N就表示原本要被删除的结点,由红黑树的属性可以推出该结点不可能有孙子结点),替代结点要么是前驱,要么是后继,但是都最多只有一个非叶子孩子结点(程序中使用的是前驱)。结点N为不同颜色时,处理方式不一样,具体如下。

结点N为红色:

若结点N为红色,那么它的孩子结点必须是黑色的,而且两个孩子结点都是叶子结点(因为结点N最多只能有一个非叶子孩子结点,所以只能是一边是一个黑色的非叶子孩子结点,另一边是一个黑色叶子结点,这样通过结点N的路径的黑色结点的数目就不一样了(因为黑色的非叶子孩子结点至少有两个黑色叶子结点),这样就违反了性质5)。此时直接将N删除即可,删除之后N被空叶子结点(NULL)替代,而NULL结点都是黑色的,所以属性5依然保持。好了,结点N为红色的情形比较简单,下面看结点N为黑色的情形。

结点N为黑色:

结点N为黑色时,可以分为两种情况:
第1种情况:若其孩子结点M有一个是非叶子结点,另外一个是黑色空叶子结点,那么非叶子结点M必须是红色。因为如果非叶子结点也是黑色的,这样通过结点N的路径的黑色结点的数目将不一样(通过空叶子结点这边的黑色结点的数目是2,而通过黑色非叶子结点这边的至少是3),由此违反了性质5。所以非叶子结点M只能是红色的,这种情况比较简单,只需用M代替N,并将M变为黑色,则不违反任何属性,此时依然是一棵合格的红黑树。

第2种情况:若两个孩子结点都为空叶子结点(叶子结点都为黑色,并且是NULL),则此时用空叶子结点NULL替代N后,通过原始替代结点N的路径的黑色结点的数目将比原来减少1,此时就要分类讨论了,将替代结点N的父结点表示为P,兄弟结点表示为S。具体分类如下:

case 1:

N是根结点,在这种情况下,我们已经做完了(仅仅通过直接返回就可以达到这个效果!),因为这种情况下整棵树只有一个结点(注意大前提:N的两个孩子结点都是空叶子结点,所以整棵树只有一个结点),要删除的结点正是根结点,直接返回之后,在上一层函数中用N的子结点(NULL)替代N,相当于将N删除掉了。

#294#_     
     |     
     ~317~ 

Deleting key 1th: 294 ---------------------------------------------------------------------

case 0 <294> (若当前结点的子结点为红色,则将子结点变成黑色) : 
#294#_     
     |     
     #317# 

case 0 <294> (将当前结点用子结点替代,之后释放当前结点) : 
#317# 

Deleting key 1th: 317 ---------------------------------------------------------------------

case 1 <317> (若当前结点是根结点,直接返回) : 
case 0 <317> (将当前结点用子结点替代,之后释放当前结点) : 
NODE is NULL



注意:在case 2,case 5,case 6中,我们假设N是父结点P的左孩子结点,如果是右孩子结点,那么“左”和“右”应该对调。

case 2:

N的兄弟结点S是红色的(从case 3开始S都是黑色的)。此时对调父结点P(肯定是黑色的)和兄弟结点S的颜色,然后将父结点P左旋。注意此时如果直接将N删掉而结束,那么P结点的左边只有一个黑色结点(NULL),而右边有两个黑色结点(S的左孩子和S的孙子结点(黑色的空叶子结点)),这样就违反了属性5,所以左旋之后将接着按case 4、case 5或case 6来处理。

        ________________#177#__________________________                    
        |                                             |                    
   _#103#______                       ________________~265~______          
   |          |                       |                         |          
#16#         _~146~_             _#206#______                  _#313#_     
             |     |             |          |                  |     |     
         #140#     #170#     #178#         _~236~_         #294#     #317# 
                                           |     |                         
                                       #208#     #253#                     

Deleting key 1th: 16 ---------------------------------------------------------------------

case 2 <16> (若当前结点的兄弟结点是红色,则将父亲和兄弟的颜色对调,若该结点靠左,则左旋父结点,若靠右则右旋父结点) : 
        ________________#177#__________________________                    
        |                                             |                    
   _~103~______                       ________________~265~______          
   |          |                       |                         |          
#16#         _#146#_             _#206#______                  _#313#_     
             |     |             |          |                  |     |     
         #140#     #170#     #178#         _~236~_         #294#     #317# 
                                           |     |                         
                                       #208#     #253#                     

左旋: 103
                  ______#177#__________________________                    
                  |                                   |                    
        ______#146#_                  ________________~265~______          
        |          |                  |                         |          
   _~103~_         #170#         _#206#______                  _#313#_     
   |     |                       |          |                  |     |     
#16#     #140#               #178#         _~236~_         #294#     #317# 
                                           |     |                         
                                       #208#     #253#                     

case 4 <16> (若当前结点的父结点为红色,兄弟结点是黑色,且兄弟结点的左右子结点都为黑色,则将兄弟结点和父结点的颜色对调) : 
                  ______#177#__________________________                    
                  |                                   |                    
        ______#146#_                  ________________~265~______          
        |          |                  |                         |          
   _#103#_         #170#         _#206#______                  _#313#_     
   |     |                       |          |                  |     |     
#16#     ~140~               #178#         _~236~_         #294#     #317# 
                                           |     |                         
                                       #208#     #253#                     

case 0 <16> (将当前结点用子结点替代,之后释放当前结点) : 
              ______#177#__________________________                    
              |                                   |                    
    ______#146#_                  ________________~265~______          
    |          |                  |                         |          
#103#_         #170#         _#206#______                  _#313#_     
     |                       |          |                  |     |     
     ~140~               #178#         _~236~_         #294#     #317# 
                                       |     |                         
                                   #208#     #253#                     



case 3:

结点N的父结点P,兄弟结点S以及S的孩子结点都是黑色的。此时简单的将S设为红色,这样做可以让通过S的所有路径都少一个黑色节点,与通过N的路径的黑色结点就相同了。但是,通过P的所有路径现在比不通过P的路径少了一个黑色节点,所以仍然违反性质5。要修正这个问题,我们要从case 1开始,在P上做重新平衡处理,并且一直递归到根结点为止,到根结点时,通过根结点的所有路径都少了一个黑色结点,然后再从case 1返回。

         ______#236#______          
         |               |          
    _#178#_             _#294#_     
    |     |             |     |     
#177#     #206#     #253#     #317# 

Deleting key 5th: 253 ---------------------------------------------------------------------

case 3 <253> (若当前结点的父结点和兄弟结点是黑色,且兄弟结点的左右子结点都为黑色,则将兄弟结点变红,将父结点转到case 1做重新平衡处理) : 
         ______#236#______          
         |               |          
    _#178#_             _#294#_     
    |     |             |     |     
#177#     #206#     #253#     ~317~ 

case 3 <294> (若当前结点的父结点和兄弟结点是黑色,且兄弟结点的左右子结点都为黑色,则将兄弟结点变红,将父结点转到case 1做重新平衡处理) : 
         ______#236#______          
         |               |          
    _~178~_             _#294#_     
    |     |             |     |     
#177#     #206#     #253#     ~317~ 

case 1 <236> (若当前结点是根结点,直接返回) : 
case 0 <253> (将当前结点用子结点替代,之后释放当前结点) : 
         ______#236#_          
         |          |          
    _~178~_         #294#_     
    |     |              |     
#177#     #206#          ~317~ 



case 4:

结点N的父结点P是红色的,兄弟结点S以及S的两个孩子结点(都是空叶子结点)都是黑色。此时将P和S的颜色对调,这不影响通过S的黑色结点数量,但是在通过N的路径上增加了一个黑色结点,正好和要删除的结点N相抵消,这样属性5被满足了。

              ________________#236#______          
              |                         |          
    ______#177#______                  _#294#_     
    |               |                  |     |     
#146#_             _~206~_         #253#     #317# 
     |             |     |                         
     ~170~     #178#     #208#                     

Deleting key 6th: 208 ---------------------------------------------------------------------

case 4 <208> (若当前结点的父结点为红色,兄弟结点是黑色,且兄弟结点的左右子结点都为黑色,则将兄弟结点和父结点的颜色对调) : 
              ________________#236#______          
              |                         |          
    ______#177#______                  _#294#_     
    |               |                  |     |     
#146#_             _#206#_         #253#     #317# 
     |             |     |                         
     ~170~     ~178~     #208#                     

case 0 <208> (将当前结点用子结点替代,之后释放当前结点) : 
              ___________#236#______          
              |                    |          
    ______#177#______             _#294#_     
    |               |             |     |     
#146#_             _#206#     #253#     #317# 
     |             |                          
     ~170~     ~178~                          




case 5:

结点N的兄弟结点S的左孩子是红色,右孩子是黑色(左红右黑),并且结点N是父结点P的左孩子。此时将S与其左孩子的颜色对调,然后右旋S。注意此时通过父结点P的路径都有相同的黑色结点数目,但是如果直接将N删掉会违反属性5(与case 2中类似),所以我们继续按照case 6来处理。

         ___________#236#______          
         |                    |          
    _#177#______             _#294#_     
    |          |             |     |     
#170#         _#206#     #253#     #317# 
              |                          
          ~178~                          

Deleting key 1th: 170 ---------------------------------------------------------------------

case 5 <170> (subcase1: 若当前结点为左子结点,兄弟结点是黑色,且兄弟结点的左子结点为红色,右子结点为黑色,则将兄弟结点和兄弟结点左孩子的颜色对调,再右旋兄弟结点) : 
         ___________#236#______          
         |                    |          
    _#177#______             _#294#_     
    |          |             |     |     
#170#         _~206~     #253#     #317# 
              |                          
          #178#                          

右旋: 206
         ___________#236#______          
         |                    |          
    _#177#_                  _#294#_     
    |     |                  |     |     
#170#     #178#_         #253#     #317# 
               |                         
               ~206~                     

case 6 <170> (将兄弟结点的颜色和父结点的颜色对调) : 
         ___________#236#______          
         |                    |          
    _#177#_                  _#294#_     
    |     |                  |     |     
#170#     #178#_         #253#     #317# 
               |                         
               ~206~                     

case 6 <170> (subcase1: 若当前结点为左子结点,则兄弟结点的右子结点必为红色,将兄弟结点的右子结点变成黑色,将父结点左旋) : 
         ___________#236#______          
         |                    |          
    _#177#_                  _#294#_     
    |     |                  |     |     
#170#     #178#_         #253#     #317# 
               |                         
               #206#                     

左旋: 177
              ______#236#______          
              |               |          
         _#178#_             _#294#_     
         |     |             |     |     
    _#177#     #206#     #253#     #317# 
    |                                    
#170#                                    

case 0 <170> (将当前结点用子结点替代,之后释放当前结点) : 
         ______#236#______          
         |               |          
    _#178#_             _#294#_     
    |     |             |     |     
#177#     #206#     #253#     #317# 
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                  ________________~265~______          
    |     |                  |                         |          
#103#     #170#         _#206#______                  _#313#_     
                        |          |                  |     |     
                    #178#         _~236~_         #294#     #317# 
                                  |     |                         
                              #208#     #253#                     

Deleting key 12th: 313 ---------------------------------------------------------------------

若结点有两个非空结点,用前驱代替它
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                  ________________~265~______          
    |     |                  |                         |          
#103#     #170#         _#206#______                  _#294#_     
                        |          |                  |     |     
                    #178#         _~236~_         #294#     #317# 
                                  |     |                         
                              #208#     #253#                     

case 3 <294> (若当前结点的父结点和兄弟结点是黑色,且兄弟结点的左右子结点都为黑色,则将兄弟结点变红,将父结点转到case 1做重新平衡处理) : 
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                  ________________~265~______          
    |     |                  |                         |          
#103#     #170#         _#206#______                  _#294#_     
                        |          |                  |     |     
                    #178#         _~236~_         #294#     ~317~ 
                                  |     |                         
                              #208#     #253#                     

case 5 <294> (subcase2: 若当前结点为右子结点,兄弟结点是黑色,且兄弟结点的左子结点为黑色,右子结点为红色,则将兄弟结点和兄弟结点右孩子的颜色对调,再左旋兄弟结点) : 
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                  ________________~265~______          
    |     |                  |                         |          
#103#     #170#         _~206~______                  _#294#_     
                        |          |                  |     |     
                    #178#         _#236#_         #294#     ~317~ 
                                  |     |                         
                              #208#     #253#                     

左旋: 206
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                            ______~265~______          
    |     |                            |               |          
#103#     #170#              ______#236#_             _#294#_     
                             |          |             |     |     
                        _~206~_         #253#     #294#     ~317~ 
                        |     |                                   
                    #178#     #208#                               

case 6 <294> (将兄弟结点的颜色和父结点的颜色对调) : 
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                            ______#265#______          
    |     |                            |               |          
#103#     #170#              ______~236~_             _#294#_     
                             |          |             |     |     
                        _~206~_         #253#     #294#     ~317~ 
                        |     |                                   
                    #178#     #208#                               

case 6 <294> (subcase2: 若当前结点为右子结点,则兄弟结点的左子结点必为红色,将兄弟结点的左子结点变成黑色,将父结点右旋) : 
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                            ______#265#______          
    |     |                            |               |          
#103#     #170#              ______~236~_             _#294#_     
                             |          |             |     |     
                        _#206#_         #253#     #294#     ~317~ 
                        |     |                                   
                    #178#     #208#                               

右旋: 265
         ______#177#________________                              
         |                         |                              
    _#146#_                  ______~236~______                    
    |     |                  |               |                    
#103#     #170#         _#206#_             _#265#______          
                        |     |             |          |          
                    #178#     #208#     #253#         _#294#_     
                                                      |     |     
                                                  #294#     ~317~ 

case 0 <294> (将当前结点用子结点替代,之后释放当前结点) : 
         ______#177#________________                         
         |                         |                         
    _#146#_                  ______~236~______               
    |     |                  |               |               
#103#     #170#         _#206#_             _#265#_          
                        |     |             |     |          
                    #178#     #208#     #253#     #294#_     
                                                       |     
                                                       ~317~ 



case 6:

结点N的兄弟结点S是黑色的,S的右孩子是红色的(右红),并且结点N是父结点P的左孩子。此时将父结点P和兄弟结点S的颜色对调,再将S的右孩子变为黑色,然后左旋父结点P。
 

            ________________#177#__________________________                         
            |                                             |                         
   _____#103#______                       ________________~265~______               
   |              |                       |                         |               
#15#_            _~146~_             _#206#______                  _#294#_          
    |            |     |             |          |                  |     |          
    ~16~     #140#     #170#     #178#         _~236~_         #293#     #313#_     
                                               |     |                        |     
                                           #208#     #253#                    ~317~ 

Deleting key 14th: 293 ---------------------------------------------------------------------

case 6 <293> (将兄弟结点的颜色和父结点的颜色对调) : 
            ________________#177#__________________________                         
            |                                             |                         
   _____#103#______                       ________________~265~______               
   |              |                       |                         |               
#15#_            _~146~_             _#206#______                  _#294#_          
    |            |     |             |          |                  |     |          
    ~16~     #140#     #170#     #178#         _~236~_         #293#     #313#_     
                                               |     |                        |     
                                           #208#     #253#                    ~317~ 

case 6 <293> (subcase1: 若当前结点为左子结点,则兄弟结点的右子结点必为红色,将兄弟结点的右子结点变成黑色,将父结点左旋) : 
            ________________#177#__________________________                         
            |                                             |                         
   _____#103#______                       ________________~265~______               
   |              |                       |                         |               
#15#_            _~146~_             _#206#______                  _#294#_          
    |            |     |             |          |                  |     |          
    ~16~     #140#     #170#     #178#         _~236~_         #293#     #313#_     
                                               |     |                        |     
                                           #208#     #253#                    #317# 

左旋: 294
            ________________#177#__________________________                         
            |                                             |                         
   _____#103#______                       ________________~265~___________          
   |              |                       |                              |          
#15#_            _~146~_             _#206#______                       _#313#_     
    |            |     |             |          |                       |     |     
    ~16~     #140#     #170#     #178#         _~236~_             _#294#     #317# 
                                               |     |             |                
                                           #208#     #253#     #293#                

case 0 <293> (将当前结点用子结点替代,之后释放当前结点) : 
            ________________#177#__________________________                    
            |                                             |                    
   _____#103#______                       ________________~265~______          
   |              |                       |                         |          
#15#_            _~146~_             _#206#______                  _#313#_     
    |            |     |             |          |                  |     |     
    ~16~     #140#     #170#     #178#         _~236~_         #294#     #317# 
                                               |     |                         
                                           #208#     #253#                     
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                  ________________~265~______          
    |     |                  |                         |          
#103#     #170#         _#206#______                  _#313#_     
                        |          |                  |     |     
                    #178#         _~236~_         #294#     #317# 
                                  |     |                         
                              #208#     #253#                     

Deleting key 12th: 313 ---------------------------------------------------------------------

若结点有两个非空结点,用前驱代替它
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                  ________________~265~______          
    |     |                  |                         |          
#103#     #170#         _#206#______                  _#294#_     
                        |          |                  |     |     
                    #178#         _~236~_         #294#     #317# 
                                  |     |                         
                              #208#     #253#                     

case 3 <294> (若当前结点的父结点和兄弟结点是黑色,且兄弟结点的左右子结点都为黑色,则将兄弟结点变红,将父结点转到case 1做重新平衡处理) : 
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                  ________________~265~______          
    |     |                  |                         |          
#103#     #170#         _#206#______                  _#294#_     
                        |          |                  |     |     
                    #178#         _~236~_         #294#     ~317~ 
                                  |     |                         
                              #208#     #253#                     

case 5 <294> (subcase2: 若当前结点为右子结点,兄弟结点是黑色,且兄弟结点的左子结点为黑色,右子结点为红色,则将兄弟结点和兄弟结点右孩子的颜色对调,再左旋兄弟结点) : 
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                  ________________~265~______          
    |     |                  |                         |          
#103#     #170#         _~206~______                  _#294#_     
                        |          |                  |     |     
                    #178#         _#236#_         #294#     ~317~ 
                                  |     |                         
                              #208#     #253#                     

左旋: 206
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                            ______~265~______          
    |     |                            |               |          
#103#     #170#              ______#236#_             _#294#_     
                             |          |             |     |     
                        _~206~_         #253#     #294#     ~317~ 
                        |     |                                   
                    #178#     #208#                               

case 6 <294> (将兄弟结点的颜色和父结点的颜色对调) : 
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                            ______#265#______          
    |     |                            |               |          
#103#     #170#              ______~236~_             _#294#_     
                             |          |             |     |     
                        _~206~_         #253#     #294#     ~317~ 
                        |     |                                   
                    #178#     #208#                               

case 6 <294> (subcase2: 若当前结点为右子结点,则兄弟结点的左子结点必为红色,将兄弟结点的左子结点变成黑色,将父结点右旋) : 
         ______#177#__________________________                    
         |                                   |                    
    _#146#_                            ______#265#______          
    |     |                            |               |          
#103#     #170#              ______~236~_             _#294#_     
                             |          |             |     |     
                        _#206#_         #253#     #294#     ~317~ 
                        |     |                                   
                    #178#     #208#                               

右旋: 265
         ______#177#________________                              
         |                         |                              
    _#146#_                  ______~236~______                    
    |     |                  |               |                    
#103#     #170#         _#206#_             _#265#______          
                        |     |             |          |          
                    #178#     #208#     #253#         _#294#_     
                                                      |     |     
                                                  #294#     ~317~ 

case 0 <294> (将当前结点用子结点替代,之后释放当前结点) : 
         ______#177#________________                         
         |                         |                         
    _#146#_                  ______~236~______               
    |     |                  |               |               
#103#     #170#         _#206#_             _#265#_          
                        |     |             |     |          
                    #178#     #208#     #253#     #294#_     
                                                       |     
                                                       ~317~ 

去吧,去吧,到彼岸去吧,彼岸是光明的世界!
原文地址:https://www.cnblogs.com/lengyue365/p/5140831.html