知识点十二:红黑树

前言

二叉查找树是最常用的一种二叉树,它支持快速插入、删除、查找操作,各个操作的时间复杂度跟树的高度成正比,理想情况下,时间复杂度是 O(logn)。不过,二叉查找树在频繁的动态更新过程中,可能会出现树的高度远大于 log2n 的情况,从而导致各个操作的效率下降。极端情况下,二叉树会退化为链表,时间复杂度会退化到 O(n)。要解决这个复杂度退化的问题,我们需要构建一种不管怎么删除、插入数据,在任何时候,都能保持任意节点左右子树都比较平衡的二叉查找树,也就是所谓的平衡二叉查找树。

很多书籍里,但凡讲到平衡二叉查找树,就会拿红黑树作为例子。不仅如此,在实际的开发工程中,很多用到平衡二叉查找树的地方都会用红黑树。有没有想过,为什么工程中都喜欢用红黑树,而不是其他平衡二叉查找树呢

平衡二叉查找树

平衡二叉树的严格定义是这样的:二叉树中任意一个节点的左右子树的高度相差不能大于 1。从这个定义来看,完全二叉树、满二叉树其实都是平衡二叉树,但是非完全二叉树也有可能是平衡二叉树。
在这里插入图片描述
平衡二叉查找树不仅满足上面平衡二叉树的定义,还满足二叉查找树的特点。最先被发明的平衡二叉查找树是AVL 树,它严格符合刚讲到的平衡二叉查找树的定义,即任何节点的左右子树高度相差不超过 1,是一种高度平衡的二叉查找树

但是,很多平衡二叉查找树并没有严格符合上面的定义(即树中任意一个节点的左右子树的高度相差不能大于 1),比如下面要讲的红黑树,它从根节点到各个叶子节点的最长路径,有可能会比最短路径大一倍。我们学习数据结构和算法是为了应用到实际的开发中的,所以没必要死抠定义。对于平衡二叉查找树这个概念,我们要从这个数据结构的由来,去理解“平衡”的意思。

发明平衡二叉查找树这类数据结构的初衷是,解决普通二叉查找树在频繁的插入、删除等动态更新的情况下,出现时间复杂度退化的问题。所以,平衡二叉查找树中“平衡”的意思,其实就是让整棵树左右看起来比较“对称”、比较“平衡”,不要出现左子树很高、右子树很矮的情况。这样就能让整棵树的高度相对来说低一些,相应的插入、删除、查找等操作的效率高一些。所以,如果我们现在设计一个新的平衡二叉查找树,只要树的高度不比 log2n 大很多(比如树的高度仍然是对数量级的),尽管它不符合我们前面讲的严格的平衡二叉查找树的定义,但我们仍然可以说,这是一颗合格的平衡二叉查找树。

红黑树

平衡二叉查找树其实有很多种类,比如:Splay Tree(伸展树)、Treap(树堆)等,但是每当提到平衡二叉查找树,听到的基本都是红黑树。它的出镜率甚至要高于“平衡二叉查找树”。红黑树的英文是“Red-Black Tree”,简称 R-B Tree。它是一种不严格的平衡二叉查找树,意思是,它的定义是不严格符合平衡二叉查找树的定义的。那红黑树究竟是怎么定义的呢?

顾名思义,红黑树中的节点,一类被标记为黑色,一类被标记为红色。它是一种含有红黑结点并能自平衡的二叉查找树,必须满足下面性质:

  1. 每个节点要么是黑色,要么是红色。
  2. 根节点是黑色的
  3. 每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存储数据;
  4. 任何相连的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的,每个红色结点的两个子结点一定都是黑色
  5. 每个节点,从该节点到达其后代所有可达叶子节点的简单路径,都包含相同数目的黑色节点,从这个性质可以进一步推出,如果一个结点存在黑子结点,那么该结点肯定有两个子结点
    在这里插入图片描述
    上图即为一颗简单的红黑树,其中Nil为叶子结点,并且它是黑色的。红黑树并不是一个完美的平衡二叉查找树,从上图可以看到,根结点P的左子树显然比右子树高,但左子树和右子树的黑结点的层数是相等的,也即任意一个结点到到每个叶子结点的路径都包含数量相同的黑色结点。所以我们叫红黑树这种平衡为黑色完美平衡

针对上述第四点性质多做一点拓展,每个红色结点的两个子结点必须都是黑色,但是黑结点的子节点可以同时包含一个红结点和一个黑结点,如下图的F节点。
在这里插入图片描述

为什么说红黑树是“近似平衡”的?

设计平衡二叉查找树的初衷,是为了解决二叉查找树因为动态更新导致的性能退化问题。所以,“平衡”的意思可以等价为性能不退化。“近似平衡”就等价为性能不会退化得太严重

二叉查找树很多操作的性能都跟树的高度成正比。一棵极其平衡的二叉树(满二叉树或完全二叉树)的高度大约是 log2n,所以如果要证明红黑树是近似平衡的,我们只需要分析,红黑树的高度是否比较稳定地趋近 log2n 就好了。接下来,我们就一起来一步一步来推导红黑树的高度。

首先,我们来看,如果我们将红色节点从红黑树中去掉,那单纯包含黑色节点的红黑树的高度是多少呢?
在这里插入图片描述
红色节点删除之后,有些节点就没有父节点了,它们会直接拿这些节点的祖父节点(即父节点的父节点)作为父节点。所以,之前的二叉树就变成了四叉树。前面红黑树的定义里有这么一条:从任意节点到可达的叶子节点的每个路径包含相同数目的黑色节点。我们从四叉树中取出某些节点,放到叶节点位置,四叉树就变成了完全二叉树。所以,仅包含黑色节点的四叉树的高度,比包含相同节点个数的完全二叉树的高度还要小。

我们知道完全二叉树的高度近似 log2n,这里的四叉“黑树”的高度要低于完全二叉树,所以去掉红色节点的“黑树”的高度也不会超过 log2n。那么,现在把红色的节点加回去,高度会变成多少呢?

从上面的定义得知,在红黑树中,红色节点不能相邻(不能相连接),也就是说,有一个红色节点就要至少有一个黑色节点,将它跟其他红色节点隔开。红黑树中包含最多黑色节点的路径不会超过 log2n,所以加入红色节点之后,最长路径不会超过 2log2n,也就是说,红黑树的最大高度近似 2log2n。所以,红黑树的高度只比高度平衡的 AVL 树的高度(log2n)仅仅大了一倍,在性能上,下降得并不多。这样推导出来的结果不够精确,实际上红黑树的性能更好。

红黑树的自平衡是如何实现的?

在插入、删除节点的过程中,第4、5点性质可能会被破坏,因此,红黑树的“平衡调整”,实际上就是要把被破坏的第4、第5点性质恢复过来。它靠的是什么?三种操作:左旋右旋变色

  • 左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
    在这里插入图片描述
  • 右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
    在这里插入图片描述
  • 变色:结点的颜色由红变黑或由黑变红。

上面所说的旋转结点也即旋转的支点,可以发现,旋转操作不会影响旋转结点的父结点,父结点以上的结构还是保持不变的。左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树挪了;右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树挪了。所以旋转操作是局部的

但要保持红黑树的性质,结点不能乱挪,还得靠变色。怎么变?具体情景有不同的变法。

红黑树插入操作的平衡调整

插入操作包括两部分工作:一是查找插入的位置(即找到待插入节点的父节点);二插入后对红黑树进行平衡调整。查找插入的父结点很简单,跟二叉查找树的查找操作区别不大:
在这里插入图片描述
现在插入位置已经找到了,把插入结点放到正确的位置就可以啦,但插入结点是应该设置为什么颜色呢?答案是红色。理由很简单,当父结点(如果存在)为黑色结点时,插入红色节点后,红黑树的黑色平衡没被破坏,不需要做自平衡操作。但如果插入结点是黑色,那么插入位置所在的子树黑色结点总是多1,必须做自平衡。而当父结点(如果存在)为红色结点时,就要分情况讨论了。

为了简化描述,这里先约定一下,把父节点的兄弟节点叫作叔叔节点,父节点的父节点叫作祖父节点。
在这里插入图片描述
在这里插入图片描述
其中,I表示插入结点,P表示插入结点的父结点,S表示插入结点的叔叔结点,PP表示插入结点的祖父结点。

插入情景1:红黑树为空树

这是最简单的一种情景,直接把插入结点作为根结点就行,但注意,根据红黑树性质2:根节点是黑色。由于前面说了,插入的结点默认设为红色的,所以插到根结点的位置时,还需要把插入结点设为黑色的。

插入情景2:插入结点的Key已存在

插入结点的Key已存在,由于红黑树总是保持平衡的,在插入前红黑树已经是平衡的,那么把插入结点I设为将当前结点的颜色,再更新当前结点的值为插入结点的值即可。

插入情景3:插入结点的父结点为黑结点

由于插入的结点是红色的,当插入结点的父节点为黑色时,并不会影响红黑树的平衡,直接插入即可,无需做自平衡处理。

插入情景4:插入结点的父结点为红结点

再次回想下红黑树的性质2:根结点是黑色。如果插入的父结点为红结点,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点。这点很重要,因为后续的旋转操作肯定需要祖父结点的参与。情景4又分为很多子情景,下面一一展开分析:

4.1:叔叔结点存在并且为红结点

从红黑树性质4可以推断,插入结点的祖父结点肯定为黑结点,因为叔叔结点和父节点均为红结点,而红黑树不可以同时存在两个相连的红结点。那么当插入新结点后,此时该插入子树的红黑层数的情况是:黑红红。最简单的处理方式是把其改为:红黑红,即把祖父结点改成红色,把父节点和叔叔结点改成黑色。此时,如果该祖父结点(以下称为PP结点)的父节点为黑色,那么无需再做任何处理;但如果PP的父结点是红色,根据性质4,此时红黑树已不平衡了,所以还需要把PP当作新的插入结点,继续做插入操作自平衡处理,直到平衡为止。
在这里插入图片描述
在这里插入图片描述
此外,如果PP结点刚好为根结点时,那我们必须把PP重新设为黑色,那么树的红黑层数结构变为:黑黑红。换句话说,从根结点到叶子结点的路径中,黑色结点的个数增加了。这也是唯一一种会增加红黑树中黑色结点层数的插入情景

我们还可以总结出一个经验:红黑树的生长是自底向上的。这点不同于普通的二叉查找树,普通的二叉查找树的生长是自顶向下的。

4.2:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点

插入前,叔叔结点非红即为叶子结点(黑色空节点Nil)。因为如果叔叔结点为黑结点,而父结点为红结点,那么叔叔结点所在的子树的黑色结点就比父结点所在子树的多了,这不满足红黑树的性质5。

需要旋转操作时,肯定是一边子树的结点多了或少了,需要租或借给另一边。插入显然是多的情况,那么把多的结点租给另一边子树就可以了。

  • 4.2.1:插入结点是其父结点的左子结点
    在这里插入图片描述
    左边两个红结点,右边不存在子结点,那么分成一边一个刚刚好,并且这里旋转后I和PP为红色,P为黑色,肯定不会破坏P以上树的平衡。那么问题来了,可以把P设为红色,I和PP设为黑色吗?但把P设为红色,显然又会出现情景4.1的情况,需要自底向上处理,做多了无谓的操作。
  • 4.2.2:插入结点是其父结点的右子结点
    这种情景显然可以转换为情景4.2.1,如下图所示。
    在这里插入图片描述

4.3:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点

这种情况对应情景4.2,只是方向反转了。

  • 4.3.1:插入结点是其父结点的右子结点
    在这里插入图片描述
  • 4.3.2:插入结点是其父结点的右子结点
    在这里插入图片描述
    上面的情景举例的都是第一次插入而不包含自底向上处理的情况,那么上面所说的情景都适合自底向上的情况吗?答案是肯定的。理由很简单,但每棵子树都能自平衡,那么整棵树最终总是平衡的。

红黑树删除操作的平衡调整

红黑树插入已经够复杂了,但删除操作更复杂,也是红黑树最复杂的操作了。红黑树的删除操作也包括两部分工作:一是查找目标结点;二是删除后进行平衡调整。查找目标结点显然可以复用查找操作,当不存在目标结点时,直接结束;当存在目标结点时,删除该结点后就得做自平衡处理了。删除了结点后我们还需要找结点来替代删除结点的位置,不然子树跟父辈结点之间就断开了,除非删除结点刚好没子结点,那么就不需要替代了。

二叉树删除结点找替代结点有3种情情景:

  1. 若删除结点无子结点,直接删除
  2. 若删除结点只有一个子结点,用子结点替换删除结点
  3. 若删除结点有两个子结点,用后继结点(大于删除结点的最小结点,也即删除结点的右子树种最左结点)替换删除结点

补充说明下,情景3中,可以拿前继结点(删除结点的左子树最左结点)替代吗?可以的。但习惯上大多都是拿后继结点来替代,后文的讲解也是用后继结点来替代。这里顺便提供一种找前继和后继结点的直观的方法:把二叉树所有结点投射在X轴上,所有结点都是从左到右排好序的,所有目标结点的前后结点就是对应前继和后继结点
在这里插入图片描述
接下来,讲一个重要的思路:删除结点被替代后,在不考虑结点的键值的情况下,对于树来说,可以认为删除的是替代结点。如下图所示,在不看键值对的情况下,红黑树最终结果是删除了Q所在位置的结点!这种思路非常重要,大大简化了后文讲解红黑树删除的情景!
在这里插入图片描述
基于此,上面所说的3种二叉树的删除情景都可以相互转换,并且最终都是转换为情景1。
在这里插入图片描述

  • 情景2:删除结点用其唯一的子结点替换,子结点替换为删除结点后,可以认为删除的是子结点,若子结点又有两个子结点,那么相当于转换为情景3,一直自顶向下转换,总是能转换为情景1。(对于红黑树来说,根据性质5,只存在一个子结点的结点肯定在树末了)
  • 情景3:删除结点用后继结点替换,该后继结点肯定不存在左结点,如果后继结点有右子结点,那么相当于转换为情景2,否则转为为情景1。

综上所述,删除操作删除的结点其实都是转换成删除替代结点,而替代结点最后总是存在于树末。有了这结论,我们讨论的删除红黑树的情景就少了很多,因为我们只考虑删除树末的叶子结点的情景了。

同样的,我们也先来总体看下删除操作的所有情景。即使简化了还是有9种情景,但跟插入操作一样,存在左右对称的情景,只是方向变了,没有本质区别。
在这里插入图片描述
同样的,我们还是先来约定下一些叫法。
在这里插入图片描述
图中的字母并不代表结点Key的大小。R表示替代结点,P表示替代结点的父结点,S表示替代结点的兄弟结点,SL表示兄弟结点的左子结点,SR表示兄弟结点的右子结点。灰色结点表示它可以是红色也可以是黑色。

值得特别提醒的是:R是即将被替换到删除结点的位置的替代结点,在删除前,它还在原来所在位置参与红黑树的自平衡,平衡处理后再替换到删除结点的位置,才算删除完成

删除情景1:替换结点是红色结点

我们把替换结点换到了删除结点的位置后,由于替换结点为红色,且总存在于树末(叶子结点),删除也了不会影响红黑树的平衡,只要把替换结点的颜色设为删除结点的颜色即可。

删除情景2:替换结点是黑结点

当替换结点是黑色时,我们就不得不进行自平衡处理了。我们必须还得考虑替换结点是其父结点的左子结点还是右子结点,来做不同的旋转操作,使树重新平衡。

2.1:替换结点是其父结点的左子结点

  • 2.1.1:替换结点的兄弟结点是红结点
    若兄弟结点是红结点,那么根据性质4,兄弟结点的父结点和子结点肯定为黑色,不会有其他情景。
    在这里插入图片描述
    这里先记住,此时R仍然是替代结点,它的新的兄弟结点SL和兄弟结点的子结点都是黑色的。
  • 2.1.2:替换结点的兄弟结点是黑结点
    当兄弟结点为黑时,其父结点和子结点的具体颜色也无法确定(如果也不考虑自底向上的情况,子结点非红即为黑色叶子结点Nil),此时又得考虑多种子情景。
    2.1.2.1:替换结点的兄弟结点的右子结点是红结点,左子结点任意颜色
    即将删除的左子树的一个黑色结点R,显然左子树的黑色结点个数比右子树少1了,然而右子树又有红色结点,那么我们可以直接向右子树“借”个红结点来补充黑色结点就好了,而且此时肯定需要用旋转处理了,使得左右两边的子树黑色结点个数均衡。
    在这里插入图片描述
    前文提醒过,这里R是即将替换的,它还参与树的自平衡,平衡后再替换到删除结点的位置,所以R最终可以看作是删除的。另外上图是考虑到第一次替换和自底向上处理的情况,如果是第一次替换的情况,根据红黑树性质,SL肯定是红色结点或为黑色叶子结点Nil,所以最终结果树是平衡的。如果是自底向上处理的情况,同样,每棵子树都保持平衡状态,最终整棵树肯定是平衡的。
    2.1.2.2:替换结点的兄弟结点的右子结点为黑结点,左子结点为红结点
    兄弟结点所在的子树有红结点,我们总是可以向兄弟子树借个红结点过来,显然该情景可以转换为情景2.1.2.1。
    在这里插入图片描述
    2.1.2.3:替换结点的兄弟结点的子结点都为黑结点
    此次兄弟子树都没红结点可“借”了,兄弟帮忙不了,那就找父母呗,这种情景我们把兄弟结点设为红色,再把父结点当作替代结点,自底向上处理,去找父结点的兄弟结点去“借”。但为什么需要把兄弟结点设为红色呢?显然是为了在P所在的子树中保证平衡(R即将删除,少了一个黑色结点,子树也需要少一个),后续的平衡工作交给父辈们考虑了,还是那句,当每棵子树都保持平衡时,最终整棵总是平衡的。
    在这里插入图片描述

2.2:替换结点是其父结点的右子结点

与前面的情况类似,只是方向相反了。

  • 2.2.1:替换结点的兄弟结点是红结点
    在这里插入图片描述
  • 2.2.2:替换结点的兄弟结点是黑结点
    2.2.2.1:替换结点的兄弟结点的左子结点是红结点,右子结点任意颜色
    在这里插入图片描述
    2.2.2.2:替换结点的兄弟结点的左子结点为黑结点,右子结点为红结点
    在这里插入图片描述
    2.2.2.3:替换结点的兄弟结点的子结点都为黑结点
    在这里插入图片描述

综上所述,红黑树删除后自平衡处理可以总结为:
1.自己能搞定的自消化(情景1)
2.自己不能搞定的叫兄弟帮忙(除了情景1、情景2.1.2.3和情景2.2.2.3外的剩余场景)
3.兄弟都帮忙不了的,通过父母,找远方亲戚(情景2.1.2.3和情景2.2.2.3)

解答开篇:为什么在工程中大家都喜欢用红黑树而非其它平衡二叉查找树?

Treap、Splay Tree在绝大部分情况下,它们操作的效率都很高,但是也无法避免极端情况下时间复杂度的退化。尽管这种情况出现的概率不大,但是对于单次操作时间非常敏感的场景来说,它们并不适用。

AVL 树是一种高度平衡的二叉树,所以查找的效率非常高,但是,有利就有弊,AVL 树为了维持这种高度的平衡,就要付出更多的代价。每次插入、删除都要做调整,就比较复杂、耗时。所以,对于有频繁的插入、删除操作的数据集合,使用 AVL 树的代价就有点高了。

而红黑树只是做到了近似平衡,并不是严格的平衡,所以在维护平衡的成本上,要比 AVL 树要低,具体体现在旋转的情况少于AVL树。所以,红黑树的插入、删除、查找各种操作性能都比较稳定。对于工程应用来说,要面对各种异常情况,为了支撑这种工业级的应用,我们更倾向于这种性能稳定的平衡二叉查找树。

小结

一、平衡二叉查找树
1.定义:二叉树中任意一个节点的左右子树的高度相差不能大于 1。
2.完全二叉树、满二叉树其实都是平衡二叉树,但是非完全二叉树也有可能是平衡二叉树。
3.作用:解决普通二叉查找树在频繁的插入、删除等动态更新的情况下,出现时间复杂度退化的问题。
4.平衡二叉查找树中“平衡”的意思,其实就是让整棵树左右看起来比较“对称”、比较“平衡”,不要出现左子树很高、右子树很矮的情况。这样就能让整棵树的高度相对来说低一些,相应的插入、删除、查找等操作的效率高一些。
5.只要所设计的树的高度不比 log2n 大很多(比如树的高度仍然是对数量级的),尽管它不符合我们前面讲的严格的平衡二叉查找树的定义,但我们仍然可以说,这是一个合格的平衡二叉查找树。
6.种类:Splay Tree(伸展树)、Treap(树堆)、AVL树、红黑树等。

二、红黑树(R-B Tree)

  1. 一棵红黑树需要满足以下几个性质:
    (1)每个节点要么是黑色,要么是红色。
    (2) 根节点是黑色的;
    (3)每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存储数据;
    (4)任何相连的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的,每个红色结点的两个子结点一定都是黑色;
    (5)每个节点,从该节点到达其可达叶子节点的所有路径,都包含相同数目的黑色节点,从这个性质可以进一步推出,如果一个结点存在黑子结点,那么该结点肯定有两个子结点;
  2. 红黑树是“近似平衡”的
    (1)平衡二叉查找树的初衷,是为了解决二叉查找树因为动态更新导致的性能退化问题。所以,“平衡”的意思可以等价为性能不退化。“近似平衡”就等价为性能不会退化的太严重。
    (2)红黑树的最大高度近似 2log2n,只比高度平衡的 AVL 树的高度(log2n)仅仅大了一倍,在性能上,下降得并不多。所以它是近似平衡的,插入、删除、查找操作的时间复杂度都是 O(logn)。
    (3)实际上,AVL 树为了维持高度的平衡,就要付出更多的代价。每次插入、删除都要做调整,就比较复杂、耗时。所以,对于有频繁的插入、删除操作的数据集合,使用 AVL 树的代价就有点高了。这时候,红黑树只是做到了近似平衡,并不是严格的平衡,所以在维护平衡的成本上,要比 AVL 树要低。
    (4)红黑树的插入、删除、查找各种操作性能都比较稳定。所以,在工程中,但凡是用到动态插入、删除、查找数据的场景,都可以用到它。

三、红黑树的自平衡
1.红黑树总是通过旋转和变色达到自平衡。
(1)左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
(2)右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
(3)变色:结点的颜色由红变黑或由黑变红。

2.红黑树插入操作的平衡调整

  • 步骤:先查找插入的位置,插入后自平衡处理
  • 8种插入情景
    (1)插入情景1:红黑树为空树
    (2)插入情景2:插入结点的Key已存在
    (3)插入情景3:插入结点的父结点为黑结点
    (4)插入情景4:插入结点的父结点为红结点
    —》插入情景4.1:叔叔结点存在并且为红结点
    —》插入情景4.2:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点
    ———》插入情景4.2.1:插入结点是其父结点的左子结点
    ———》插入情景4.2.2:插入结点是其父结点的右子结点
    —》插入情景4.3:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点
    ———》插入情景4.3.1:插入结点是其父结点的右子结点
    ———》插入情景4.3.2:插入结点是其父结点的右子结点

3.红黑树删除操作的平衡调整

  • 步骤:先查找目标结点;删除后自平衡处理。
  • 删除结点找替代结点有3种情情景:
    (1)情景1:若删除结点无子结点,直接删除
    (2)情景2:若删除结点只有一个子结点,用子结点替换删除结点
    (3)情景3:若删除结点有两个子结点,用后继结点(大于删除结点的最小结点)替换删除结点
    上面3种二叉树的删除情景可以相互转换并且最终都是转换为情景1,删除操作可以看作删除替代结点,而替代结点最后总是在树末。
  • 删除操作的9种情景:
    删除情景1:替换结点是红色结点
    删除情景2:替换结点是黑结点
    —》删除情景2.1:替换结点是其父结点的左子结点
    ——》删除情景2.1.1:替换结点的兄弟结点是红结点
    ——》删除情景2.1.2:替换结点的兄弟结点是黑结点
    ———》删除情景2.1.2.1:替换结点的兄弟结点的右子结点是红结点,左子结点任意颜色
    ———》删除情景2.1.2.2:替换结点的兄弟结点的右子结点为黑结点,左子结点为红结点
    ———》删除情景2.1.2.3:替换结点的兄弟结点的子结点都为黑结点
    —》删除情景2.2:替换结点是其父结点的右子结点
    ——》删除情景2.2.1:替换结点的兄弟结点是红结点
    ——》删除情景2.2.2:替换结点的兄弟结点是黑结点
    ———》删除情景2.2.2.1:替换结点的兄弟结点的左子结点是红结点,右子结点任意颜色
    ———》删除情景2.2.2.2:替换结点的兄弟结点的左子结点为黑结点,右子结点为红结点
    ———》删除情景2.2.2.3:替换结点的兄弟结点的子结点都为黑结点

思考题

  1. 动态数据结构支持动态地数据插入、删除、查找操作,除了红黑树,还有哪些动态数据结构呢?能对比一下各自的优势、劣势,以及应用场景吗?
  2. 请画出下图的插入自平衡处理过程
    在这里插入图片描述
  3. 请画出下图的删除自平衡处理过程
    在这里插入图片描述

参考

《数据结构与算法之美》
王争
前Google工程师

另外,推荐这篇博客,图文并茂,讲解得非常详细:https://www.jianshu.com/p/e136ec79235c
红黑树的代码实现:https://github.com/bary321/hongheishu

发布了34 篇原创文章 · 获赞 1 · 访问量 473

猜你喜欢

转载自blog.csdn.net/Mr_SCX/article/details/103712134