思考红黑树

前言

        网上讲红黑树的文章比较多,参考  维基百科--红黑树  《教你彻底理解红黑树》  等等。大概扫了扫,在讲红黑树的插入和删除操作的时候,主要集中精力在讲插入和删除的各种复杂情况,然后再画几个图出来,作者就以为讲得很清楚很牛逼了。其实我觉得,讲得非常烂,算法最重要的是它背后的思想,或者说它背后一些规律。前面提到的两篇文章都在讲“这个算法是什么”,但是“这个算法为什么是这样”压根就没仔细分析,例如红黑树删除的时候,为什么主要的那四种情况下要选择那样处理,我觉得这非常重要。当然我不敢保证我讲得就不烂,我也只是根据自己的总结分析,去猜测“为什么是这样”的答案,欢迎拍砖。
       下面先在文章前面先把一些基本的东西贴一贴,引自维基百科。另外建议读者在看红黑树算法的时候,一定要自己亲自动手画。

红黑树的概念

红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由鲁道夫·贝尔发明的,他称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目。

红黑树的性质

红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 节点是红色或黑色。
性质2. 根是黑色。
性质3. 所有叶子都是黑色(叶子是NIL节点)。
性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

理解红黑树添加和删除的关键点

1.左旋和右旋:
左旋和右旋有什么好处呢? 左旋和右旋并不改变红黑树的排序性质,即旋转后它还是一棵二叉查找树!下面会提到旋转带来的另外的效果!

2.这里我需要创造几个概念:“黑深度”、红黑树“稳定”、“颜色左旋”、“颜色右旋”。
   a.黑深度”:就是 指包括整棵树(或子树)的根节点在内,到叶子节点的最长路经中,黑节点个数。这个与黑高度有什么不同呢?黑高度计算的时候是不包括这棵树(或者子树)的根节点的。而我下面的讨论中是要把树(或子树)的根节点算上的,所以搞了“黑深度”这个概念。
    举个例子:

    例如上图,整棵树的左子树的“黑深度”为3(最长的路经为BEH或BEI,有3个黑节点),整棵树的右子树的“黑深度”为1(最长路经为CF或CG,有1个黑节点)
    b. 红黑树“稳定”:是指整棵树从根节点到叶子节点的所有路经中,黑节点个数都一样。(也就是红黑树的性质5),例如上图就是不稳定的树,需要调整,上图的左子树是不“稳定”的,因为“黑深度”取值有2,3,3,两种取值,上图的右子树是稳定的,每条路径的“黑深度”都为1,所以是“稳定”的。
    c.颜色的左旋和右旋      //看个图就明白了:
   
   颜色的右旋就是上图的逆过程。

    好吧,这几个是我自己用到的概念,下面举个简单的例子来说明这几个概念的用武之地。

        总结一下这两步操作带来的效果:
        通过节点的左旋和黑颜色的左旋,可以使左子树的“黑深度”+1,右子树“黑深度”-1,并且整棵树的“黑深度”跟原来的一样!(这个配合红节点可以染黑,能使得整棵树又恢复稳定!后面会讲到)
        通过节点左旋的和红颜色的左旋,不会改变原来整棵树的“稳定”状态,只会改变红节点的位置。)即原来是“稳定的”,操作后还是“稳定”的,如果原来是不“稳定”的,操作后也是不“稳定的”。(这个可以用来调整把红节点调整到其它地方而不改变原来整棵树的性质!)
  

        调整红黑树的思想1:尽量局部调整,保持局部树的“稳定”而不去破坏树的其它部分!

        下面举另外一个例子来引出调整红黑树的另一个思想:旋转时利用可染黑的红节点!
         上面提到,节点左旋和颜色左旋带来的效果,那就有个问题了,如果原来左右子树的“黑深度”差值为1,那么旋转完差值还是1啊,并没有变成稳定的。这个怎么破?利用红节点!直接看图:
        

        从上图中可以看到,虽然旋转后左右子树的“黑深度”差1,但是,右子树有可以拿来利用的红节点!红节点染黑之后,左右子树的黑深度就相同了!也就是稳定了!

        调整红黑树的思想2:利用可以染黑的红节点!

        有了上面两种简单的思想,下面理解红黑树的添加和删除操作的各种情况应该非常简单了。

红黑树的插入

        从总体上先分析一下:
        添加的节点可以为红色,也可以为黑色,选哪种好呢?选黑色的话,意味着整棵树的黑高度+1,违反了性质5,那么要调整整棵树的其它很多路径,显然特别麻烦。选红色呢?如果该节点的父节点为红色,那就违反了性质4.《算法导论》里面的算法采用的是插入的节点选为红色,为什么不选黑色呢?选黑色不行吗?下面说明,选黑色也是可以的,但是情况最终会转换为选红的一样!如下图所示:
        

下面开始正式讨论红黑树的插入的几种情况:
    a.插入后该节点为根节点,很显然,染黑即可...
    b.插入后,该节点的父节点为黑色,很显然,不用任何处理...
    我们要讨论的是插入后,父节点为红色的情况:
    1.叔叔节点为红色





        从上面的图可以看到,情况1的最终流向,要么是结束,要么就是最终转换为情况2。

        2.叔叔节点为黑色
        前面讨论过,通过节点的旋转和红颜色的旋转,是能改变红节点的位置,并且保持原来树的稳定性的!
        



红黑树的删除

        红黑树节点的删除只是比红黑树节点的添加稍微复杂一点点而已!
        首先应先去复习复习二叉查找树的删除操作。分几种情况:
        1.欲删除的节点最多只有一个孩子节点,则直接删除该节点,并把孩子节点或者空与该节点的父节点相连。
        2.欲删除的节点有两个孩子,则使用它的后继(或者前驱)节点覆盖该节点,然后删除其后继(或者前驱)节点,而由于其后继(或者前驱)节点最多只有一个孩子节点,所以删除操作最多只传递一次。这里注意,使用前驱或者后继节点去覆盖原节点都是一样的,网上有的版本是使用前驱节点去覆盖,《算法导论》使用的是用后继节点去覆盖的,这里选择用后继节点去覆盖。

         好了,从上面的分析中,可以知道删除的时候,最终真正删除的节点是最多只有一个孩子的节点。我们先把简单的情况干掉,再来讨论复杂的情况:
a.欲删除的节点是红色的,显然,把该节点直接删掉,把孩子节点或者NIL节点接回去就ok了,什么性质都没有违反。
b.欲删除的节点是黑色的,那么删掉它的话,显然某些路径会少一个黑节点,性质5被破坏。这个有什么好办法解决呢?
掐脚趾头想想就知道了:
1.既然我某棵子树的黑高度少1,把整棵树其它跟我不一样黑高度的,都通通给我减1。这个办法好,简单粗暴。
2.上面的办法1有些粗暴,要是让所有其它子树都减1有点困难的话呢...那就想办法把我自己这颗子树的黑高度+1不就完了,等等,怎么加呢?用红节点变黑节点!
        下面开始讨论。既然要讨论,那就要分各种情况讨论,那么根据什么去分情况呢?根据父节点?还是其它节点?显然从总体上看,我们删除了一个节点,那肯定是在局部的一棵树中(这个最小的局部的树的树根就是当前节点的父节点。),左右子树的”黑深度“相差1。这个要怎么办呢?用之前讨论过的,旋转根节点并旋转颜色来解决。我们首先根据兄弟的颜色来划分情况。
         注意,下面的“当前节点“是指删除该节点后接回去的那个节点(也就是删除的节点的唯一孩子节点)。下图中的白色节点代表任意颜色。并且当前节点与兄弟节点的左右关系式对称的,下面以当前节点在左边,兄弟节点在右边为例讨论。

情况1:兄弟节点为红色,那么父节点也就肯定是黑色了(性质4)


    从上图可以知道,情况1其实是可以转成情况2的。

情况2:兄弟节点为黑色。且左子树比右子树”黑深度“少1。这个算算还可以分几种情况,根据兄弟节点的两孩子颜色分,2*2=4有四种情况呢。
         先看兄弟节点左右孩子均为黑色的情况。




从上图可以看出,情况2.1最终由两种情况能结束,有一种情况是回到最初的问题。

情况3:兄弟节点为黑色,且兄弟节点的孩子至少有一个是红色。


这种情况很好处理,利用之前分析过的,左旋+左旋黑色,可以使左子树“黑深度”+1,右子树“黑深度”-1,且整棵树的稳定性不变,旋转后,变为右子树“黑深度”比左子树少1,但是右子树有个红节点!利用可以染黑的红节点,使左右子树的“黑深度”变得一致!

还有两种情况还没分析:
1.兄弟节点为黑色,左孩子为红色,右孩子为黑色;
2.兄弟节点为黑色,左右孩子均为红色。
想一想就应该明白,右旋一下就能转换为上面的情况了,因为只需要保证右边能腾出一个红节点拿来染黑就ok了!分析结束!

        
最后,欢迎转载,欢迎拍砖。   






        



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值