本文转自安卓大叔算法
当在10亿数据中只须要进行10几回比较就能查找到目标时,不由感叹编程之魅力!人类之伟大呀! —— 学红黑树有感。编程
终于,在学习了几天的红黑树相关的知识后,我想把我所学所想和所感分享给你们。红黑树是一种比较难的数据结构,要彻底搞懂很是耗时耗力,红黑树怎么自平衡?何时须要左旋或右旋?插入和删除破坏了树的平衡后怎么处理?等等一连串的问题在学习前困扰着我。若是你在学习过程当中也会存在个人疑问,那么本文对你会有帮助,本文帮助你全面、完全地理解红黑树!数据结构
本文将经过图文的方式讲解红黑树的知识点,而且不会涉及到任何代码,相信我,在懂得红黑树实现原理前,看代码会一头雾水的,当原理懂了,代码也就循序渐进写而已,没任何难度。学习
阅读本文你需具有知识点:网站
事不宜迟,让咱们进入正题吧。3d
红黑树也是二叉查找树,咱们知道,二叉查找树这一数据结构并不难,而红黑树之因此难是难在它是自平衡的二叉查找树,在进行插入和删除等可能会破坏树的平衡的操做时,须要从新自处理达到平衡状态。如今在脑海想下怎么实现?是否是太多情景须要考虑了?啧啧,先别急,经过本文的学习后,你会以为,其实也不过如此而已。好吧,咱们先来看下红黑树的定义和一些基本性质。blog
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须知足下面性质:get
从性质5又能够推出:源码
图1就是一颗简单的红黑树。其中Nil为叶子结点,而且它是黑色的。(值得提醒注意的是,在Java中,叶子结点是为null的结点。io
红黑树并非一个完美平衡二叉查找树,从图1能够看到,根结点P的左子树显然比右子树高,但左子树和右子树的黑结点的层数是相等的,也即任意一个结点到到每一个叶子结点的路径都包含数量相同的黑结点(性质5)。因此咱们叫红黑树这种平衡为黑色完美平衡。
介绍到此,为了后面讲解不至于混淆,咱们还须要来约定下红黑树一些结点的叫法,如图
咱们把正在处理(遍历)的结点叫作当前结点,如图2中的D,它的父亲叫作父结点,它的父亲的另一个子结点叫作兄弟结点,父亲的父亲叫作祖父结点。
前面讲到红黑树能自平衡,它靠的是什么?三种操做:左旋、右旋和变色。
图3 左旋
上面所说的旋转结点也即旋转的支点,图4和图5中的P结点。
咱们先忽略颜色,能够看到旋转操做不会影响旋转结点的父结点,父结点以上的结构仍是保持不变的。
左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树挪了。
右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树挪了。
因此旋转操做是局部的。另外能够看出旋转能保持红黑树平衡的一些端详了:当一边子树的结点少了,那么向另一边子树“借”一些结点;当一边子树的结点多了,那么向另一边子树“租”一些结点。
但要保持红黑树的性质,结点不能乱挪,还得靠变色了。怎么变?具体情景又不一样变法,后面会具体讲到,如今只须要记住红黑树老是经过旋转和变色达到自平衡。
balabala了这么多,相信你对红黑树有必定印象了,那么如今来考考你:
思考题1:黑结点能够同时包含一个红子结点和一个黑子结点吗? (答案见文末)
接下来先讲解红黑树的查找热热身。
由于红黑树是一颗二叉平衡树,而且查找不会破坏树的平衡,因此查找跟二叉平衡树的查找无异:
如图5所示。
很是简单,但简单不表明它效率很差。正因为红黑树总保持黑色完美平衡,因此它的查找最坏时间复杂度为O(2lgN),也即整颗树恰好红黑相隔的时候。能有这么好的查找效率得益于红黑树自平衡的特性,而这背后的付出,红黑树的插入操做功不可没~
插入操做包括两部分工做:一查找插入的位置;二插入后自平衡。查找插入的父结点很简单,跟查找操做区别不大:
如图6所示。
ok,插入位置已经找到,把插入结点放到正确的位置就能够啦,但插入结点是应该是什么颜色呢?答案是红色。理由很简单,红色在父结点(若是存在)为黑色结点时,红黑树的黑色平衡没被破坏,不须要作自平衡操做。但若是插入结点是黑色,那么插入位置所在的子树黑色结点老是多1,必须作自平衡。
全部插入情景如图7所示。
嗯,插入情景不少呢,8种插入情景!但情景一、2和3的处理很简单,而情景4.2和情景4.3只是方向反转而已,懂得了一种情景就能推出另一种情景,因此整体来看,并不复杂,后续咱们将一个一个情景来看,把它完全搞懂。
另外,根据二叉树的性质,除了情景2,全部插入操做都是在叶子结点进行的。这点应该不难理解,由于查找插入位置时,咱们就是在找子结点为空的父结点的。
在开始每一个情景的讲解前,咱们仍是先来约定下,如图8所示。
图8的字母并不表明结点Key的大小。I表示插入结点,P表示插入结点的父结点,S表示插入结点的叔叔结点,PP表示插入结点的祖父结点。
好了,下面让咱们一个一个来分析每一个插入的情景以其处理。
最简单的一种情景,直接把插入结点做为根结点就行,但注意,根据红黑树性质2:根节点是黑色。还须要把插入结点设为黑色。
处理:把插入结点做为根结点,并把结点设置为黑色。
插入结点的Key已存在,既然红黑树总保持平衡,在插入前红黑树已是平衡的,那么把插入结点设置为将要替代结点的颜色,再把结点的值更新就完成插入。
处理:
因为插入的结点是红色的,当插入结点的黑色时,并不会影响红黑树的平衡,直接插入便可,无需作自平衡。
处理:直接插入。
再次回想下红黑树的性质2:根结点是黑色。若是插入的父结点为红结点,那么该父结点不可能为根结点,因此插入结点老是存在祖父结点。这点很重要,由于后续的旋转操做确定须要祖父结点的参与。
情景4又分为不少子情景,下面将进入重点部分,各位看官请留神了。
插入情景4.1:叔叔结点存在而且为红结点
从红黑树性质4能够,祖父结点确定为黑结点,由于不能够同时存在两个相连的红结点。那么此时该插入子树的红黑层数的状况是:黑红红。显然最简单的处理方式是把其改成:红黑红。如图9和图10所示。
处理:
能够看到,咱们把PP结点设为红色了,若是PP的父结点是黑色,那么无需再作任何处理;但若是PP的父结点是红色,根据性质4,此时红黑树已不平衡了,因此还须要把PP看成新的插入结点,继续作插入操做自平衡处理,直到平衡为止。
试想下PP恰好为根结点时,那么根据性质2,咱们必须把PP从新设为黑色,那么树的红黑结构变为:黑黑红。换句话说,从根结点到叶子结点的路径中,黑色结点增长了。这也是惟一一种会增长红黑树黑色结点层数的插入情景。
咱们还能够总结出另一个经验:红黑树的生长是自底向上的。这点不一样于普通的二叉查找树,普通的二叉查找树的生长是自顶向下的。
插入情景4.2:叔叔结点不存在或为黑结点,而且插入结点的父亲结点是祖父结点的左子结点
单纯从插入前来看,也即不算情景4.1自底向上处理时的状况,叔叔结点非红即为叶子结点(Nil)。由于若是叔叔结点为黑结点,而父结点为红结点,那么叔叔结点所在的子树的黑色结点就比父结点所在子树的多了,这不知足红黑树的性质5。后续情景一样如此,再也不多作说明了。
前文说了,须要旋转操做时,确定一边子树的结点多了或少了,须要租或借给另外一边。插入显然是多的状况,那么把多的结点租给另外一边子树就能够了。
插入情景4.2.1:插入结点是其父结点的左子结点
处理:
由图11可得,左边两个红结点,右边不存在,那么一边一个刚恰好,而且由于为红色,确定不会破坏树的平衡。
咦,能够把PP设为红色,I和P设为黑色吗?答案是能够!看过《算法:第4版》的同窗可能知道,书中讲解的就是把PP设为红色,I和P设为黑色。但把PP设为红色,显然又会出现情景4.1的状况,须要自底向上处理,作多了无谓的操做,既然能本身消化就不要麻烦祖辈们啦~
插入情景4.2.2:插入结点是其父结点的右子结点
这种情景显然能够转换为情景4.2.1,如图12所示,不作过多说明了。
处理:
插入情景4.3:叔叔结点不存在或为黑结点,而且插入结点的父亲结点是祖父结点的右子结点
该情景对应情景4.2,只是方向反转,不作过多说明了,直接看图。
插入情景4.3.1:插入结点是其父结点的右子结点
处理:
插入情景4.3.2:插入结点是其父结点的右子结点
处理:
好了,讲完插入的全部情景了。可能又同窗会想:上面的情景举例的都是第一次插入而不包含自底向上处理的状况,那么上面所说的情景都适合自底向上的状况吗?答案是确定的。理由很简单,但每棵子树都能自平衡,那么整棵树最终老是平衡的。好吧,在出个习题,请你们拿出笔和纸画下试试(请务必动手画下,加深印象):
习题1:请画出图15的插入自平衡处理过程。(答案见文末)
红黑树插入已经够复杂了,但删除更复杂,也是红黑树最复杂的操做了。但稳住,胜利的曙光就在前面了!
红黑树的删除操做也包括两部分工做:一查找目标结点;而删除后自平衡。查找目标结点显然能够复用查找操做,当不存在目标结点时,忽略本次操做;当存在目标结点时,删除后就得作自平衡处理了。删除告终点后咱们还须要找结点来替代删除结点的位置,否则子树跟父辈结点断开了,除非删除结点恰好没子结点,那么就不须要替代。
二叉树删除结点找替代结点有3种情情景:
补充说明下,情景3的后继结点是大于删除结点的最小结点,也是删除结点的右子树种最左结点。那么能够拿前继结点(删除结点的左子树最左结点)替代吗?能够的。但习惯上大多都是拿后继结点来替代,后文的讲解也是用后继结点来替代。另外告诉你们一种找前继和后继结点的直观的方法(不知为什么没人提过,你们都知道?):把二叉树全部结点投射在X轴上,全部结点都是从左到右排好序的,全部目标结点的先后结点就是对应前继和后继结点。如图16所示。
接下来,讲一个重要的思路:删除结点被替代后,在不考虑结点的键值的状况下,对于树来讲,能够认为删除的是替代结点!话很苍白,咱们看图17。在不看键值对的状况下,图17的红黑树最终结果是删除了Q所在位置的结点!这种思路很是重要,大大简化了后文讲解红黑树删除的情景!
基于此,上面所说的3种二叉树的删除情景能够相互转换而且最终都是转换为情景1!
二叉树删除结点情景关系图如图18所示。
综上所述,删除操做删除的结点能够看做删除替代结点,而替代结点最后老是在树末。有了这结论,咱们讨论的删除红黑树的情景就少了不少,由于咱们只考虑删除树末结点的情景了。
一样的,咱们也是先来整体看下删除操做的全部情景,如图19所示。
哈哈,是的,即便简化了仍是有9种情景!但跟插入操做同样,存在左右对称的情景,只是方向变了,没有本质区别。一样的,咱们仍是来约定下,如图20所示。
图20的字母并不表明结点Key的大小。R表示替代结点,P表示替代结点的父结点,S表示替代结点的兄弟结点,SL表示兄弟结点的左子结点,SR表示兄弟结点的右子结点。灰色结点表示它能够是红色也能够是黑色。
值得特别提醒的是,R是即将被替换到删除结点的位置的替代结点,在删除前,它还在原来所在位置参与树的子平衡,平衡后再替换到删除结点的位置,才算删除完成。
万事具有,咱们进入最后的也是最难的讲解。
咱们把替换结点换到了删除结点的位置时,因为替换结点时红色,删除也了不会影响红黑树的平衡,只要把替换结点的颜色设为删除的结点的颜色便可从新平衡。
处理:颜色变为删除结点的颜色
当替换结点是黑色时,咱们就不得不进行自平衡处理了。咱们必须还得考虑替换结点是其父结点的左子结点仍是右子结点,来作不一样的旋转操做,使树从新平衡。
删除情景2.1:替换结点是其父结点的左子结点
删除情景2.1.1:替换结点的兄弟结点是红结点
若兄弟结点是红结点,那么根据性质4,兄弟结点的父结点和子结点确定为黑色,不会有其余子情景,咱们按图21处理,获得删除情景2.1.2.3(后续讲解,这里先记住,此时R仍然是替代结点,它的新的兄弟结点SL和兄弟结点的子结点都是黑色)。
处理:
删除情景2.1.2:替换结点的兄弟结点是黑结点
当兄弟结点为黑时,其父结点和子结点的具体颜色也没法肯定(若是也不考虑自底向上的状况,子结点非红即为叶子结点Nil,Nil结点为黑结点),此时又得考虑多种子情景。
删除情景2.1.2.1:替换结点的兄弟结点的右子结点是红结点,左子结点任意颜色
即将删除的左子树的一个黑色结点,显然左子树的黑色结点少1了,然而右子树又又红色结点,那么咱们直接向右子树“借”个红结点来补充黑结点就好啦,此时确定须要用旋转处理了。如图22所示。
处理:
平衡后的图怎么不知足红黑树的性质?前文提醒过,R是即将替换的,它还参与树的自平衡,平衡后再替换到删除结点的位置,因此R最终能够看做是删除的。另外图2.1.2.1是考虑到第一次替换和自底向上处理的状况,若是只考虑第一次替换的状况,根据红黑树性质,SL确定是红色或为Nil,因此最终结果树是平衡的。若是是自底向上处理的状况,一样,每棵子树都保持平衡状态,最终整棵树确定是平衡的。后续的情景同理,不作过多说明了。
删除情景2.1.2.2:替换结点的兄弟结点的右子结点为黑结点,左子结点为红结点
兄弟结点所在的子树有红结点,咱们老是能够向兄弟子树借个红结点过来,显然该情景能够转换为情景2.1.2.1。图如23所示。
处理:
删除情景2.1.2.3:替换结点的兄弟结点的子结点都为黑结点
好了,这次兄弟子树都没红结点“借”了,兄弟帮忙不了,找父母呗,这种情景咱们把兄弟结点设为红色,再把父结点看成替代结点,自底向上处理,去找父结点的兄弟结点去“借”。但为何须要把兄弟结点设为红色呢?显然是为了在P所在的子树中保证平衡(R即将删除,少了一个黑色结点,子树也须要少一个),后续的平衡工做交给父辈们考虑了,仍是那句,当每棵子树都保持平衡时,最终整棵老是平衡的。
处理:
删除情景2.2:替换结点是其父结点的右子结点
好啦,右边的操做也是方向相反,不作过多说明了,相信理解了删除情景2.1后,确定能够理解2.2。
删除情景2.2.1:替换结点的兄弟结点是红结点
处理:
删除情景2.2.2:替换结点的兄弟结点是黑结点
删除情景2.2.2.1:替换结点的兄弟结点的左子结点是红结点,右子结点任意颜色
处理:
删除情景2.2.2.2:替换结点的兄弟结点的左子结点为黑结点,右子结点为红结点
处理:
删除情景2.2.2.3:替换结点的兄弟结点的子结点都为黑结点
处理:
综上,红黑树删除后自平衡的处理能够总结为:
哈哈,是否是跟现实中很像,当咱们有困难时,首先先本身解决,本身无力了总兄弟姐妹帮忙,若是连兄弟姐妹都帮不上,再去找远方的亲戚了。这里记忆应该会好记点~
最后再作个习题加深理解(请不熟悉的同窗务必动手画下):
***习题2:请画出图29的删除自平衡处理过程。
耗时良久,终于写完了~本身加深了红黑树的理解的同时,也但愿能帮助你们。若是你以前没学习过红黑树,看完这篇文章后可能还存在不少疑问,若是有疑问能够在评论区写出来,我会尽本身所能解答。另外给你们推荐一个支持红黑树在线生成的网站,来作各类情景梳理颇有帮助:在线生成红黑树。(删除操做那个把替代结点看做删除结点思路就是我本身在用这个网站时本身顿悟的,我以为这样讲解更容易理解。)
少了代码是否是以为有点空虚?哈哈,后续我会写关于Java和HashMap和TreeMap的文章,里面都有红黑树相关的知识。相信看了这篇文章后,再去看Java和HashMap和TreeMap的源码绝对没难度!
最后来看下思考题和习题的答案吧。
思考题1:黑结点能够同时包含一个红子结点和一个黑子结点吗?
答:能够。以下图的F结点:
习题1:请画出图15的插入自平衡处理过程。
答:
习题2:请画出图29的删除自平衡处理过程。
答: