数据结构和算法之红黑树删除(5)

文章出处:数据结构和算法之红黑树删除(5)

关注码农爱刷题,刷大厂面试题,看更多技术文章!!

六、红黑树(接前篇)

     删除场景问题分析

     上一篇文章讲述了红黑树的规范及红黑树的插入后的自平衡处理场景,在节点插入操作过程中,我们默认插入节点为红,然后判断是否需要进行平衡操作,那么今天就来看一下红黑树的删除操作需要考虑哪些情况。

     对于红黑树,删除场景的自平衡处理比较复杂,网上讲解的文章很多,看了很多篇文章,感觉都没有把问题完全说透的,也可能是作者理解没到位。本文作者尽自己的理解,把红黑树删除场景的自平衡处理给大家讲清楚。在讲解之前,对于平衡二叉查找树和红黑树规范不清楚的,请先看作者前述文章:数据结构和算法之平衡二叉查找树 和 数据结构和算法之红黑树规范及插入场景处理

     从二叉查找树相关知识可以知道,节点删除通常有几种场景分类,红黑树作为平衡二叉查找树的一种,其节点删除也适用;结合红黑树的特点,我们把这几种场景总结如下:  

图片

     基于以上场景,我们先进行具体分析:

     总场景1.1,待删除节点是红色的,红色节点的减少不会影响红黑树任何路径的黑节点数,因而直接删除即可,无需做任何平衡处理

图片

     场景1.2,待删除节点是黑色的,黑色节点的减少使红黑树经过该节点的路径的黑节点数减少,违反了红黑树规范5,因而需做平衡处理,处理方案后面内容会讲到

图片

     场景2,待删除节点是黑色的且有一红子节点,黑色节点的减少会使红黑树经过该节点的路径的黑节点数减少,会违反红黑树规范5,因而需做平衡处理,具体处理方式很简单直接用其红色子节点顶替上去并改变其颜色为黑色即可

图片

     场景3,待删除目标节点被后继节点直接替换,真正要发生删除操作的位置是后继节点;不论后继节点是左子树的最大值节点,还是右子树的最小值节点,根据二叉查找树的特性可知道,其必然只有两种形态:要么无子节点,要么只有一个特定方向的子节点,如果有两个子节点,那么后继节点必然不是最大值或最小值;所以后继节点删除的平衡处理场景,转变为无子节点的场景1和只有一个子节点的场景2的平衡处理,如下图;理解这点很重要,有益于理清后续问题处理思路。     

图片

     对于场景1.1场景2,平衡处理很简单,前文已经给出平衡处理方案,而场景3的问题处理已转变为场景1场景2的问题,所以最后我们只剩下场景1.2要处理和讲解,网上大多文章没有讲明白讲透或者没有讲解正确,特别是对上溯到上级节点去处理,基本没有讲而是一句话带过,当然也可能是作者的理解没有到位,但是还是希望在本文给大家讲明白。

      删除场景的平衡处理

     对于场景1.2,要删除的目标节点是没有子节点的黑色节点(对于总场景3来说即后继节点),黑节点的删除意味着其所在路径的黑色节点数的减少,要处理这种情况,从大的方面来讲,通常有两种策略:

     策略一:从其他分支寻找节点,通过旋转和变色(对于这两种方式还不了解的,请查看作者以前的文章),替代和弥补目标节点被删除的影响,使目标节点原本所在路径黑色节点数恢复到删除前的平衡。

     策略二:通过旋转和变色使后继节点以外的其他所有路径黑色节点数都减1,从而和后继节点原本所在路径的黑色节点数维持一致。

     同时,对这一场景问题的处理,我们可以遵循以下原则:

    处理原则一尽量优先考虑在最小分支范围内的解决方案。因为越往纵橫方向蔓延,要处理的场景越复杂。 

     处理原则二优先采用变色方法处理失衡;处理不了,才结合旋转来处理。因为红黑树之所以提出来就是希望通过变色处理来降低AVL树(也是平衡二叉查找树的一种)自平衡的旋转次数,以此满足大规模频繁增删节点的场景。  

      策略一和策略二的处理方向刚好相反,策略一是恢复到目标节点被删除前的平衡状态,策略二则是让其他所有路径黑色节点数都减少从而和目标节点原本所在的路径的黑色节点数达成平衡。那什么时候使用策略一的方案,又在什么情况下使用策略二的方案?因为策略一方案的核心是从其他分支上或共享路径上借用节点来恢复删除前的平衡,具体借用的方法可以变色或旋转;所以采用策略一方案的前提是其他分支或共享路径上有足够数额的节点可借用,即其他分支的节点数在进行变色和旋转操作后既能维持自身不失衡同时又能满足目标分支所在路径的平衡恢复。例如,其他分支全是黑色节点,则无法挪用,因为挪用其他路径上黑色节点数必然失衡;或者有红色节点,但旋转后其分支剩余节点无法维持自身的平衡。

     如果策略一方案不能处理,我们可以考虑使用策略二的处理方案;但这里我们先讲策略一的具体场景和处理逻辑。

      策略一的具体场景讲解

      策略一的思路:先从兄弟节点分支考虑,无法解决再上溯考虑父节点的兄弟叔节点的分支

      我们先看看目标节点(或后继节点)和其父节点以及兄弟节点的几种场景(后续图中G-代表祖父节点  P-代表父节点   U-代表叔节点  B-代表兄弟节点  D-删除节点即前文说的后继节点  BS-代表兄弟节点子节点):

      1. 策略一场景1-1:红父黑兄无子节点

图片

     场景处理逻辑:直接变换兄弟节点和父节点的颜色,通过利用父节点的共享属性,使从父节点开始通往兄弟节点和原本删除节点位置的两条路径的黑节点数都维持不变

     2.策略一场景1-2:红父黑兄,兄有一红色子节点

图片

     场景处理逻辑:先右旋兄弟子节点BS和兄弟节点B,然后整个兄弟分支向左旋,使父节点P成为BS节点的左分支,兄弟节点B成为其右分支;变更BS节点颜色为黑色、B节点颜色为红色

      此处再说明下:红色子节点BS也可能是B节点的右分支,如果是右分支则第一步的右旋就不用了;总之这种场景下,子节点和父节点不是同一方向的分支,需要先旋转调整成和父节点一致,然后再考虑整个分支的旋转;整个分支旋转的方向原则一般是:父子节点皆左向右旋,父子节点皆右向左旋。此处不对节点的位置不同进行更明细的场景划分,关键是掌握处理的思想和方法。

        3.策略一场景1-3:红父黑兄,兄有两红色子节点

图片

     场景处理逻辑:直接左旋整个兄弟分支,使父节点P成为兄弟节点B的左节点,兄弟子节点BS1成为父节点P的右节点;变更除BS1以外节点的颜色如图,至此整个分支平衡恢复到D节点删除前状态

     上图兄弟分支如果为左分支,则右旋;对此,本文不再进行场景细分;另外,对于黑色兄弟节点来说,在此处只能有红色子节点,如果有黑色子节点,则红黑树在目标节点(或后继节点)被删除之前就已失衡。

      4.策略一场景1-4:黑父红兄两黑色子节点

图片

       场景处理逻辑:直接左旋整个兄弟分支,使父节点P成为兄弟节点B的左节点,兄弟子节点BS1成为父节点P的右节点;变更B和BS1颜色如图,至此整个分支平衡恢复到D节点删除前状态

     同样上图兄弟分支如果为左分支,则右旋;对此,本文不再进行场景细分。此场景同前一场景1-3,就是最后变颜色的节点不同,其他都一样。另外,此场景下,红色兄弟节点只能有两个黑色子节点这种形态,如有其他形态则红黑树在目标节点删除之前就已失衡或违反红黑树规范。

    5.策略一场景1-5:黑父兄一红子节点

图片

      场景处理逻辑:旋转逻辑同策略场景1-2,只是最后的变色都变为黑色,因为此场景子分支上各路径都是两个黑色节点

      6.策略一场景1-6:黑父兄两红子节点

图片

      场景处理逻辑:旋转逻辑同策略场景1-3和1-4相似,只是最后的变色不同,此场景子分支各路径上也都需要是两个黑色节点

   7.策略一场景1-7:黑父兄无子节点

图片

      场景处理逻辑:策略一方案无法处理,兄弟分支无冗余节点可借用,要满足两条路径都有两个黑色节点,至少三个节点,节点D删除后整个父分支只剩下两个节点了,所以只能上溯到父亲的兄弟——叔节点分支去处理

     上述看似场景很多,让人眼花缭乱,但细想一下,其实总结起来就两种场景:第一种场景就是策略一场景1-7,即策略一方案不能处理的场景;其他六种场景1-(1~6)可以归纳为第二种场景,即策略一方案能处理的场景:其包含的六种或更多明细场景大同小异,没必要死记,你只要知道通过变色和旋转最终肯定都能恢复到目标节点被删除前的平衡状态即可;一个原则,如果父节点为红,则只需要保证各路径只有一个黑色节点,如果父节点为黑,则需要保证各路径有且有两个黑色节点。

      策略一场景1-7则无法再按策略一方案处理,于是我们需要上溯递归到父亲兄弟叔分支来处理。不管是采用策略一还是策略二,网上的文章基本没有对上溯到上级分支后的场景进行阐述,但是上溯到上级分支后的节点变多关系变得更复杂,父分支范围内的场景过于简单,并不能完全解决上溯到上级分支后的场景,所以作者觉得有必要进行深入讲解。

      对于叔分支,我们先看祖父节点为红色的场景:祖父为红节点,则说明父分支和叔分支各条路径上有且只有两个黑色节点,处在父分支和叔分支共享路径的红色祖父节点并没有为它们共享黑色节点数,而今父分支黑色节点数失衡,变红色祖父节点为黑色来弥补父分支黑色节点数的损失是天然的选择,也符合我们优先采用变色方式处理的原则;为了保证节点数的平衡,还需要同时通过变色递减叔分支各路径黑色节点数以及把兄弟节点变为红色,各种形态的处理场景如下列各图。

     8策略一场景1-8:父分支全黑红祖父

图片

图片

图片

      场景处理逻辑:以上红祖父条件下三种形态,通过变色各节点,父分支或叔分支黑色节点数恢复或维持和节点D删除前平衡状态一致

     再分析祖父节点为黑色时叔分支的场景:祖父为黑节点,则说明父分支和叔分支原本各条路径都是三个黑色节点,其中黑祖父作为共享节点为它们贡献了一个黑色节点。所以目标节点D被删除后,要让其路径上的黑色节点数恢复到被删除以前(这里要理解黑色节点即使被删除,其所在路径还存在,因为所有路径的结束是叶子节点为终点的,对叶子节点还不清楚的,建议先看前一篇文章 数据结构和算法之树形结构(4)),无法通过变色做到,则只能把祖父节点G旋转到父分支来弥补,而叔节点U要替代祖父节点G的位置作为共享节点(叔节点如果为红色则要变成黑色),最终对于黑色祖父叔分支的场景处理逻辑是

      1. 把叔分支向父分支方向旋转,使叔节点U保持黑颜色替代祖父节点G的位置并成为祖父节点G的父节点; 同时,把叔节点的某一分支转变为祖父节点G的反向分支(具体看旋转方向);

       2. 改变兄弟节点B的颜色为红色,父节点P颜色则不变; 最后改变叔分支剩余节点的颜色使其维持平衡

  9策略一场景1-9:父分支全黑、黑祖父

图片

图片

图片

图片

     以上图例列举了黑祖父场景下叔分支的各种常见形态,还有其他的形态没有列举处理,但处理的方法一致:左旋或右旋,然后变色。

     综上所述,对于叔分支来说,可以策略一方案处理的就两种大的场景:如果是红祖父场景,通过变色就能修复父分支的失衡;如果是黑祖父场景,就通过旋转和组合能能修复父分支的失衡。

     另外还有一些形态,到叔分支来说,按策略一方案无法处理,例如下图两种形态:

图片

     对于上图两种形态,因为叔分支节点数额不足,如果按前述方式旋转,其自身剩余节点不能维持本身平衡,则不能按策略一处理;所以则只能继续上溯到祖父节点兄弟叔祖父分支处理,以此类推,往上的处理逻辑同叔分支,此处不再重复阐述。

     对于策略一的场景和处理逻辑就讲解到这里,看似场景很多,其实归纳起来也就几句话:

     1.无论哪个层面的兄弟分支,只要共享路径的父节点是红色,就可以只通过变色完成平衡恢复;例如策略一场景1-1和叔分支的1-8场景。

     2.无论哪个层面的兄弟分支,只要共享路径的父节点是黑色,就需要通过旋转和变色组合来完成平衡恢复。例如场景1-2到6和场景1-9。

     3.剩余的不能处理的场景(这种场景下兄弟分支主要分支一般全为黑色)上溯到上一层的兄弟分支处理,直到根二代结束。

    总之,主要是记住处理的方法思路和处理的原则,不要去硬记各种场景。

      策略二的具体场景讲解

     剩下的部分主要讲述策略二的解决思路及场景。讲述之前,怕读者读到这里忘记了,特别再强调一下,本文主要场景案例和处理逻辑始终是围绕着总场景1.2来讲的,即删除没有子节点的黑色目标节点。策略二的主要思路是递减其他路径上黑色节点数,从而使所有路径上的黑色节点数达到一致;具体处理逻辑主要是通过变色来处理;主要针对的还是各层级兄弟分支的兄弟节点,对于目标节点D来说,就是它的兄弟节点B,对于它父节点P层面来说,就是目标节点D的叔节点U,再往上就是祖父兄弟叔祖父节点,以此类推。这和策略一刚好相反,策略一是想通过增加节点数来恢复平衡,所以重点关注的共享路径的各级父节点,策略二则是重点关注各级兄弟节点,因为其思路是要让各级兄弟分支黑色节点数减少即减1。我们依然以叔分支为例,大致场景分以下情况:

   1策略一场景2-1:兄弟节点为黑色

图片

图片

      场景处理逻辑:以上两种常见形态,通过变色各级兄弟节点为红色递减各路径黑色节点数,从而使各路径黑色节点数一致;其他形态处理方法大多同此例

    2策略一场景2-2:兄弟节点为红色

图片

      场景处理逻辑:通过变色各级兄弟分支其他黑色节点为红色递减各路径黑色节点数,从而使各路径黑色节点数一致;其他类似形态处理方法参考此例     

    3策略一场景2-3:层级过多的兄弟节点

图片

图片

     此处两张图演示的都是叔分支层级过多的场景,上面的图展示的是:如果直接变色处理成每条路径上两个黑色节点,会发现最下两层出现红红相邻的情况,违反红黑树规范4;所以不能直接变色,这种通常需要从叔分支向父分支旋转,然后再变色。

     场景处理逻辑:从叔分支向父分支旋转,然后变色;此处只变换了目标节点兄弟节点和一堂兄弟节点的颜色为红色,其他节点不用变色,从而使各路径黑色节点数一致;其他类似形态处理方法参考此例

     所以当要处理的各级兄弟分支子孙节点过多的时候,场景2-1和场景2-3都需要参考场景2-3处理,即先旋转各级兄弟分支再变色。同样再回到策略一,也存在这样的情况,可参考此处逻辑处理。

        行文到此,关于红黑树删除的自平衡处理就讲完了;本文基于平衡二叉查找树的删除规范,主要讲述了红黑树删除的总场景1.2即删除无子节点的黑色节点的自平衡处理(其他场景过于简单,在文章开头部分简单阐述了)的两种策略思路。总之,希望对大家有帮助,也希望自己讲清楚了、没有让读者失望!有不当之处,也欢迎留言指正!

     最最后,作一个总结:红黑树的删除自平衡处理,最终要处理的总场景只有三种,其他两个场景处理简单,最重要的是总场景1.2(即删除无子节点的黑色节点)的处理;处理策略有二:

    策略一:从其他分支寻找节点,通过旋转和变色,替代和弥补目标节点被删除的影响,使目标节点原本所在路径黑色节点数恢复到删除前的平衡。

     策略二:通过旋转和变色使后继节点以外的其他所有路径黑色节点数都减1,从而和后继节点原本所在路径的黑色节点数维持一致

      其中策略二单独采用的话,需要往上递归处理红黑树所有路径,在大规模数据频繁增删节点的业务场景下,作者认为性能不佳;而策略一则不需要往上递归遍历处理所有路径,最优的情况下直接变换父节点和兄弟节点下的颜色即可(参见策略一场景1-1),时间复杂度为O(1),所以两种策略,作者倾向于策略一方式去实现。

     另外一种思路是,策略一和策略二是否可以组合使用?有关后续代码实现和通用逻辑的提炼,如果有更多的读者关注码农爱刷题公众号,并在公众号这篇文件下留言要求,作者将酌情写红黑树后续文章,否则按计划写数据结构和算法后续文章!敬请理解!!

码农爱刷题

为计算机编程爱好者和从业人士提供技术总结和分享 !为前行者蓄力,为后来者探路!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值