目录
一. 前言
大家应该都学过平衡二叉树(AVLTree),了解到AVL树的性质,其实平衡二叉树最大的作用就是查找,AVL树的查找、插入和删除在平均和最坏情况下都是O(logN)。AVL树的效率就是高在这个地方。如果在AVL树中插入或删除结点后,使得高度之差大于1。此时,AVL树的平衡状态就被破坏,它就不再是一棵二叉树;为了让它重新维持在一个平衡状态,就需要对其进行旋转处理,那么创建一颗平衡二叉树的成本其实不小。这个时候就有人开始思考,并且提出了红黑树的理论,红黑树在业界应用很广泛,比如 Java 中的 TreeMap,JDK 1.8 中的 HashMap、C++ STL 中的 map 均是基于红黑树结构实现的。那么红黑树到底比AVL树好在哪里?
二. 为什么需要红黑树
对于二叉搜索树,如果插入的数据是随机的,那么它就接近平衡的二叉树,平衡的二叉树,它的操作效率(查询,插入,删除)效率较高,时间复杂度是O(logN)。但是可能会出现一种极端的情况,那就是插入的数据是有序的(递增或者递减),那么所有的结点都会在根结点的右侧或左侧,此时,二叉搜索树就变为了一个链表,它的操作效率就降低了,时间复杂度为O(N),所以可以认为二叉搜索树的时间复杂度介于O(logN)和O(N)之间,视情况而定。
那么为了应对这种极端情况,红黑树就出现了,它是具备了某些特性的二叉搜索树,能解决非平衡树问题,红黑树是一种接近平衡的二叉树(说它是接近平衡因为它并没有像AVL树的平衡因子的概念,它只是靠着满足红黑结点的5条性质来维持一种接近平衡的结构,进而提升整体的性能,并没有严格的卡定某个平衡因子来维持绝对平衡)。
三. 红黑树的特性
在讲解红黑树性质之前,先简单了解一下几个概念:
parent:父结点
sibling:兄弟结点
uncle:叔父结点( parent 的兄弟结点)
grand:祖父结点( parent 的父结点)
首先,红黑树是一个二叉搜索树,它在每个结点增加了一个存储位记录结点的颜色,可以是RED,也可以是BLACK;通过任意一条从根到叶子简单路径上颜色的约束,红黑树保证最长路径不超过最短路径的二倍,因而近似平衡(最短路径就是全黑结点,最长路径就是一个红结点一个黑结点,当从根结点到叶子结点的路径上黑色结点相同时,最长路径刚好是最短路径的两倍)。它同时满足以下特性:
1.根结点是黑色
2.结点是红色或黑色
3.叶子结点(空结点or外部结点)都是黑色。这里的叶子结点指的是最底层的空结点(外部结点),下图中的那些null结点才是叶子结点,null结点的父结点在红黑树里不将其看作叶子结点
4.红色结点的子结点都是黑色。红色结点的父结点都是黑色,从根结点到叶子结点的所有路径上不能有 2 个连续的红色结点
5.从任一结点到叶子结点的所有路径都包含相同数目的黑色结点

根据上面的性质,我们来判断一下下面这课树是不是红黑树

上面这棵树首先很容易就能知道是满足性质1-4条的,关键在于第5条性质,可能乍一看好像也是符合,但实际就会陷入一个误区,直接将图上的最后一层的结点看作叶子结点,这样看的话每一条从根结点到叶子结点的路径确实都经过了3个黑结点。
但实际上,在红黑树中真正被定义为叶子结点的,是那些空结点,如下图:

这样一来,路径1有4个黑色结点(算上空结点),路径2只有3个黑色结点,这样性质5就不满足了,所以这棵树并不是一个红黑树。
注:下面的讲解图中将省略红黑树的null结点,请自行脑补。
四. 红黑树的效率
4.1 红黑树效率
红黑树的查找,插入和删除操作,时间复杂度都是O(logN)。
查找操作时,它和普通的相对平衡的二叉搜索树的效率相同,都是通过相同的方式来查找的,没有用到红黑树特有的特性。
但如果插入的时候是有序数据,那么红黑树的查询效率就比二叉搜索树要高了,因为此时二叉搜索树不是平衡树,它的时间复杂度O(N)。
插入和删除操作时,由于红黑树的每次操作平均要旋转一次和变换颜色,所以它比普通的二叉搜索树效率要低一点,不过时间复杂度仍然是O(logN)。总之,红黑树的优点就是对有序数据的查询操作不会慢到O(logN)的时间复杂度。
4.2 红黑树和AVL树的比较
1. AVL树的时间复杂度虽然优于红黑树,但是对于现在的计算机,CPU太快,可以忽略性能差异
2. 红黑树的插入删除比AVL树更便于控制操作
3. 红黑树整体性能略优于AVL树(红黑树旋转情况少于AVL树)
五. 红黑树的等价变换

上面这颗红黑树,我们将所有的红色结点上移到和他们的父结点同一高度上,就会形成如下结构:

这个结构很明显,就是一棵四阶B树(一个结点最多放三个数据),如果画成如下的样子大家应该就能看的更清晰了。

由上面的等价变换我们就可以得到如下结论:
1. 红黑树 和 4阶B树(2-3-4树)具有等价性
2. 黑色结点与它的红色子结点融合在一起,形成1个B树结点
3. 红黑树的黑色结点个数 与 4阶B树的结点总个数相等
4. 在所有的B树结点中,永远是黑色结点是父结点,红色结点是子结点。黑色结点在中间,红色结点在两边。
我们可以利用四阶B树与红黑树等价的性质,以红黑树转换成B树之后的结点情况来进行一个分类:

六. 红黑树的操作
红黑树的基本操作和其他树形结构一样,一般都包括查找、插入、删除等操作。前面说到,红黑树是一种自平衡的二叉查找树,既然是二叉查找树的一种,那么查找过程和二叉查找树一样,比较简单,这里不再赘述。相对于查找操作,红黑树的插入和删除操作就要复杂的多。尤其是删除操作,要处理的情况比较多,下面就来分情况讲解。
6.1 旋转操作
在分析插入和删除操作前,先说明一下旋转操作,这个操作在后续操作中都会用得到。旋转操作分为左旋和右旋,左旋是将某个结点旋转为其右孩子的左孩子,而右旋是结点旋转为其左孩子的右孩子。这话听起来有点绕,所以还是请看下图:

上图包含了左旋和右旋的示意图,这里以右旋为例进行说明,右旋结点 M 的步骤如下:
1. 将结点 M 的左孩子引用指向结点 E 的右孩子。
2. 将结点 E 的右孩子引用指向结点 M,完成旋转。

旋转操作本身并不复杂,上面分析了右旋操作,左旋操作与此类似,只是右旋转的逆操作。
6.2 插入操作
红黑树的插入过程和二叉查找树插入过程基本类似,不同的地方在于,红黑树插入新结点后,需要进行调整,以满足红黑树的性质。
性质1规定红黑树结点的颜色要么是红色要么是黑色,那么在插入新结点时,这个结点应该是红色还是黑色呢?答案是红色,原因也不难理解。如果插入的结点是黑色,那么这个结点所在路径比其他路径多出一个黑色结点,这个调整起来会比较麻烦(参考红黑树的删除操作,就知道为啥多一个或少一个黑色结点时,调整起来这么麻烦了)。如果插入的结点是红色,此时所有路径上的黑色结点数量不变,仅可能会出现两个连续的红色结点的情况。这种情况下,通过变色和旋转进行调整即可,比之前的简单多了。所以插入的时候将结点设置为红色,可以保证满足性质 1、2、3、5 ,只有性质4不一定满足,需要进行相关调整。如果是添加根结点,则将结点设定为黑色。
6.2.1. 插入操作的所有情况
我们在分析红黑树各种插入情况的时候,将其等价转换为B树,这样我们能够更直观的进行分类,首先确定几条性质:
1. B树中,新元素必定是添加到叶子结点中(最底层的结点)
2. 4阶B树所有结点的元素个数 x 都符合 1 ≤ x ≤ 3

在上一章节红黑树的等价变换中,我们讲到了红黑树转换成B树总共有四种情况,也就是上图中叶子结点这四种情况,那么在我们进行插入操作的时候,会将结点插入到所有的叶子结点中,总共就会有12种情况,其中四种情况满足红黑树的性质,8种情况不满足红黑树性质。
6.2.1.1. 满足红黑树性质4
有 4 种情况满足红黑树的性质 4 :parent 为黑色结点。这四种情况不需要做任何额外的处理。

6.2.1.2. 不满足红黑树性质4
有 8 种情况不满足红黑树的性质 4 :parent 为红色结点( Double Red ),其中左面4种属于B树结点上溢的情况(一个4阶B树结点中最多存放三个数,这四种情况本来已经有3个了,又插入了1个,变成了4个,超出了4阶B树结点的容量范围,这种情况称为上溢)。这八种情况需要进行额外的处理。

6.2.2 LL和RR插入情况

如上图,插入52和60的位置分别是RR情况和LL情况。
RR情况:父结点为祖父结点的右结点,插入结点为父结点的右结点
LL情况:父结点为祖父结点的左结点,插入结点为父结点的左结点
这两种情况很明显,插入结点为红色,父结点也为红色,父结点的子结点为红色显然违背了红黑树的性质四,我们需要对这种情况进行修复,使其重新满足红黑树性质。
判定条件:uncle 不是红色结点。
这里的两种情况,他们的插入结点都是没有叔父结点的,所以叔父结点也不可能是红色。
案例修复:
我们在红黑树等价转换那一章节也讲过了,红黑树等价转换成B树之后,B树结点的中间结点(父结点)都是黑色,两边的结点(子结点)都是红色。但是上面两种情况插入后,插入位置的B树结点并不满足这个条件,所以我们对其进行修复,使其满足B树结点的条件之后,也就重新恢复了红黑树性质。
B树结点中的中间结点大小介于两个子结点之间。以上图RR情况为例,插入结点52的原父结点应该放在B树结点中间的位置,应当将其染成黑色。插入结点52的原祖父结点46,应当将其转换为插入结点原父结点的子结点,所以将其染成红色。LL情况同理。
完成染色之后,需要对原祖父结点进行单旋操作,来进行父结点,子结点的重新分配。以上图为例:
RR情况应该原祖父结点46左旋,将插入结点的原父结点50旋转到中间的位置。
LL情况应当原祖父结点76右旋,将插入结点的原父结点72旋转到中间的位置。
修复之后的结果如下图:

修复步骤总结:
1. parent 染成黑色,grand 染成红色
2. grand 进行单旋操作。LL:右旋转,RR:左旋转
6.2.3 LR和RL插入情况

如上图,插入48和74的位置分别是RL情况和LR情况。
RL情况:父结点为祖父结点的右结点,插入结点为父结点的左结点
LR情况:父结点为祖父结点的左结点,插入结点为父结点的右结点
这两种情况和上面的两种情况一样,插入结点为红色,父结点也为红色,父结点的子结点为红色显然违背了红黑树的性质四,我们需要对这种情况进行修复,使其重新满足红黑树性质。
判定条件:uncle 不是红色结点。
这两种情况的插入结点也是没有叔父结点的。
案例修复:
B树结点中的中间结点大小介于两个子结点之间。以上图RL情况为例,插入结点48大小介于原父结点和原祖父结点之间,它应该是B树结点中的中间结点,所以将插入结点48染成黑色,将原祖父结点46染成红色来作为插入结点的子结点。LR情况同理
完成染色之后,需要进行双旋操作,来进行父结点,子结点的重新分配。以上图为例:
1. RL情况应该原父结点50右旋,将插入结点48上移到原父结点50的高度,然后将插入结点的原祖父结点46进行左旋,将插入结点48移动到中间位置,成为中间结点。
2. LR情况应该原父结点72左旋,将插入结点74上移到原父结点72的高度,然后将插入结点的原祖父结点76进行右旋,将插入结点74移动到中间位置,成为中间结点。
修复之后的结果如下图:

修复步骤总结:
1. 插入结点染成黑色,grand 染成红色
2. 进行双旋操作
2.1. LR:parent 左旋转, grand 右旋转
2.2. RL:parent 右旋转, grand 左旋转
6.2.4 上溢的LL插入情况

如上图,插入10的位置是上溢的LL情况。
上溢LL情况:父结点为祖父结点的左结点,插入结点为父结点的左结点。并且构成的新的B树结点已经超过了B树结点容量大小范围。
这种情况和之前非上溢的四种情况一样,插入结点为红色,父结点也为红色,父结点的子结点为红色显然违背了红黑树的性质四,我们需要对这种情况进行修复,使其重新满足红黑树性质。
判定条件:uncle 是红色结点。满足这个条件的就都是上溢的情况,上溢的修复只需要染色,不需要旋转。
案例修复:
像这种上溢的情况,就需要从溢出的B树结点中选出一个结点进行向上合并,选择B树结点中中间的树去进行向上合并,这里中间的两个结点就是原父结点17和原祖父结点25,选这两个哪一个向上合并都是对的,但是我们最好选择以后方便操作的,很显然,应该选择原祖父结点25来进行向上合并,因为向上合并就是和最上层的38和55来组合成新的B树结点,向上合并的结点肯定是一个子结点,需要与上层相连,而原祖父结点25本身就已经和上层连接了,相对更加方便后续的操作。原祖父结点向上合并后,将其染成红色。
原祖父结点25向上合并后,它原来左右两边的结点需要分裂成两个子树,也就是原父结点17和插入结点10形成一个子树,原叔父结点33形成一个子树。这两个分裂形成的树都是以后25的子树。左边的子树由原父结点作为中间结点,染成黑色,右边的子树由原叔父结点作为中间结点,染成黑色。
修复之后的结果如下图:

修复步骤总结:
parent、uncle 染成黑色
grand 向上合并。将向上合并的grand染成红色,相对上一层,就当做是新添加的结点,再次来一遍插入情况的判断,进行处理。
grand 向上合并时,可能继续发生上溢。这种情况就继续递归调用修复方法就可以了。若上溢持续到根结点,只需将根结点染成黑色即可(这个意思就是说断向上上溢,一直上溢到了B树的根结点位置了,只需要将向上合并的结点变成黑色作为红黑树的根结点即可。因为从B树根结点选择出来上溢的结点,肯定就是作为整个红黑树的根结点了)。
6.2.5 上溢的RR插入情况

如上图,插入36的位置是上溢的RR情况。
上溢RR情况:父结点为祖结结点的右结点,插入结点为父结点的右结点。并且构成的新的B树结点已经超过了B树结点容量大小范围。
判定条件:uncle 是红色结点
案例修复:
上溢RR情况的修复,和上溢LL情况基本一致,只是修复的位置不同,这里中间的两个结点就是原父结点33和原祖父结点25,选择原祖父结点25来进行向上合并,原祖父结点向上合并后,将其染成红色。
原祖父结点25向上合并后,它原来左右两边的结点需要分裂成两个子树,也就是原父结点33和插入结点36形成一个子树,原叔父结点17形成一个子树。这两个分裂形成的树都是以后25的子树。左边的子树由原叔父结点作为中间结点,染成黑色,右边的子树由原父结点作为中间结点,染成黑色。
修复之后的结果如下图:

修复步骤总结:
1. parent、uncle 染成黑色
2. grand 向上合并。染成红色(其实染成红色就已经是完成了向上合并,因为祖父结点和祖父结点的父结点的连接指向并没有变),当做是新添加的结点进行处理 。
6.2.6 上溢的LR插入情况

如上图,插入20的位置是上溢的LR情况。
上溢LR情况:父结点为祖父结点的左结点,插入结点为父结点的右结点。并且构成的新的B树结点已经超过了B树结点容量大小范围。
判定条件:uncle 是红色结点
案例修复:
上溢LR情况的修复,和其他上溢情况基本一致,只是修复的位置不同,这里中间的两个结点就是原父结点17和原祖父结点25,选择原祖父结点25来进行向上合并,原祖父结点向上合并后,将其染成红色。
原祖父结点25向上合并后,它原来左右两边的结点需要分裂成两个子树,也就是原父结点17和插入结点20形成一个子树,原叔父结点33形成一个子树。这两个分裂形成的树都是以后25的子树。左边的子树由原父结点作为中间结点,染成黑色,右边的子树由原叔父结点作为中间结点,染成黑色。
修复之后的结果如下图:

修复步骤总结:
1. parent、uncle 染成黑色
2. grand 向上合并。染成红色,当做是新添加的结点进行处理
6.2.7 上溢的RL插入情况

如上图,插入30的位置是上溢的RL情况。
上溢RL情况:父结点为祖父结点的右结点,插入结点为父结点的左结点。并且构成的新的B树结点已经超过了B树结点容量大小范围。
判定条件:uncle 是红色结点
案例修复:
上溢RL情况的修复,和其他上溢情况基本一致,只是修复的位置不同,这里中间的两个结点就是原父结点33和原祖父结点25,选择原祖父结点25来进行向上合并,原祖父结点向上合并后,将其染成红色。
原祖父结点25向上合并后,它原来左右两边的结点需要分裂成两个子树,也就是原父结点33和插入结点30形成一个子树,原叔父结点17形成一个子树。这两个分裂形成的树都是以后25的子树。左边的子树由原叔父结点作为中间结点,染成黑色,右边的子树由原父结点作为中间结点,染成黑色。
修复之后的结果如下图:

修复步骤总结:
1. parent、uncle 染成黑色
2. grand 向上合并。染成黑色,当做是新添加的结点进行处理
6.2.8 插入情况总结
插入一共有12种情况:
1. 插入结点的父结点是黑色的情况有4种。这种情况仍然会维持红黑树的性质,则不需要进行额外处理。
2. 插入结点的父结点是红色的情况有8种。这种情况不满足红黑树的性质4,需要进行额外的修复处理。
这8种情况中:
1. 叔父结点不是红色的情况有4种。这些情况都是非上溢,需要通过重新染色和旋转来进行修复
2. 叔父结点是红色的情况有4种。这些情况都是上溢的,只需要通过祖父结点上溢合并和染色即可完成修复
6.3 删除操作
相较于插入操作,红黑树的删除操作则要更为复杂一些。B树中,最后真正被删除的元素都在叶子结点中。所以在红黑树中,被删除的结点一定也在最后一层。

6.3.1. 删除操作的所有情况
上面我们说删除结点一定都在最后一层,最后一层有红色结点和黑色结点,我们就以删除结点的颜色来区分删除操作的所有情况。

6.3.1.2 删除黑色结点
有3种情况:
1. 拥有 2 个红色子结点的黑色结点。不可能被直接删除,因为会找它的子结点替代删除,因此不用考虑这种情况
2. 拥有 1 个红色子结点的黑色结点
3. 黑色叶子结点

6.3.2 删除拥有1个红色子结点的黑色结点

删除拥有1个红色子结点的黑色结点的情况,是需要我们做相关的处理的。这里删除的就是结点46和76,他们只有一个红色子结点。
对于一个二叉树来说,删除一个度为1的结点(度指的是一个结点的子结点个数),将其删除后需要用它唯一的子结点来进行替换。而红黑树的这种情况的判定条件,就是判定要替代删除结点的子结点是不是红色
判定条件:用以替代的子结点是红色结点
案例修复:

删除黑色结点46和76
第一步:

将46与父结点的连接断开
第二步:

46唯一的红色子结点50作为代替46的结点,将其与46的父结点进行连接
第三步:

断开46与50的连接,将46删除
删除结点76的过程与删除结点46相同
第一步:

第二步:

第三步:

但是现在我们发现,80是红色结点,它的子结点72还是红色结点,这样明显不符合红黑树的性质,还需要进一步修复。

将替代的子结点染成黑色即可保持红黑树性质,修复完成。
修复步骤总结:
1. 用删除结点的唯一子结点对其进行替代
2. 将替代结点染成黑色
6.3.3 删除黑色叶子结点——删除结点为根结点
一棵红黑树只有一个黑色根结点(也就是唯一的一个叶子结点,整个红黑树只有这一个黑色结点),可直接删除该结点,无需做其他操作。
6.3.4 删除黑色叶子结点——删除结点的兄弟结点为黑色
讲这种删除情况前先举一个例子

上面这个我们要删除结点88,该结点为黑色叶子结点,它的兄弟结点是黑色76。从B树的角度来看,如果删除88,因为四阶B树的结点中最少存有1个元素,如果不足,则会造成下溢。也就是需要从88的兄弟结点中借一个子结点出来。这就是这一节我们讨论的删除情况的核心修复思想。
6.3.4.1 兄弟结点至少有1个红色子结点
下面三个图分别对应着兄弟结点至少有一个红色子结点的三种情况。删除结点为88,为黑色叶子结点,它的兄弟结点是76,为黑色。兄弟结点76都至少有一个红色子结点,三种情况分别为76拥有一个红色右子结点,76拥有一个红色左子结点,76拥有两个红色子结点。因为兄弟结点有红色子结点,所以可以借出一个结点来进行修复。

这三种情况,黑色叶子结点被删除后,会导致B树结点下溢(比如删除88),就可以从兄弟结点中借出一个红色子结点来进行修复。
判定条件:兄弟结点至少有 1 个红色子结点
案例修复:
1、兄弟结点有一个右子结点:

先将88结点删除

删掉之后,从B树的角度来看就出现了下溢,这个时候就需要父结点下来,在兄弟结点的子结点中找一个,将他升上去代替。具体的实现就是要对结点进行旋转。

我们可以看出,80、76、78组成的树是一个LR的情况,先对76进行左旋转(可以将76看作父结点),这样78就上去了,再对80进行右旋转(可以将80看成祖父结点),80就下去了。

旋转完了之后,如上图。将旋转完之后的中心结点(就是78、76、80组成的树的最中心的结点,这里就是78)进行重新染色,继承删除结点的父结点80的颜色。最后再将78、76、80组成的树的左右两个结点染成黑色即可完成修复。
2、兄弟结点有一个左子结点:

先将88结点删除

删掉之后,从B树的角度来看就出现了下溢,这个时候就需要父结点下来,在兄弟结点的子结点中找一个,将他升上去代替。具体的实现就是要对结点进行旋转。

我们可以看出,80、76、72组成的树是一个LL的情况,直接对80进行右旋(将80看成是祖父结点)。

旋转完了之后,如上图。将旋转完之后的中心结点(就是76、72、80组成的树的最中心的结点,这里就是76)进行重新染色,继承删除结点的父结点80的颜色。最后再将76、72、80组成的树的左右两个结点染成黑色即可完成修复。
3、兄弟结点有两个左右子结点:

先将88结点删除

删除之后,其实可以有两种旋转可以进行修复,既可以使用LL方式进行旋转,也可以使用LR方式进行旋转。但是因为LL方式只需要旋转一次,我们就选用LL方式。

直接对80进行右旋

旋转完了之后,如上图。将旋转完之后的中心结点(就是78、72、76、80组成的树的最中心的结点,这里就是76)进行重新染色,继承删除结点的父结点80的颜色。最后再将78、72、76、80组成的树的左右两个结点染成黑色即可完成修复。
修复步骤总结:
1. 进行旋转操作
2. 旋转之后的中心结点继承父结点(删除结点的父结点)的颜色
3. 旋转之后的左右结点染为黑色
6.3.4.2 兄弟结点没有红色子结点
当删除结点的兄弟结点没有红色结点可以借出的情况下,就需要父结点来向下合并进行修复,父结点向下和兄弟结点合并成新的B树结点来解决下溢。
判定条件:兄弟结点没有1个红色子结点
案例修复:
1、父结点为红色:

删除结点88,出现下溢

因为兄弟结点76没有可以借出的红色结点,所以需要父结点80来向下与76合并进行修复

将兄弟结点76染成红色,父结点80染成黑色即可完成修复
2、父结点为黑色:

删除结点88,删除之后结点88就会出现下溢

删除之后父结点80应该向下合并进行修复,但是因为父结点80为黑色,如果向下合并之后,其实就相当于80这个结点也出现了下溢。

这个时候只需要把父结点当作被删除的结点进行处理即可。
修复步骤总结:
1. 父结点向下与兄弟结点进行合并
2. 将兄弟染成红色、父结点染成黑色即可修复红黑树性质。如果父结点是黑色,直接将父结点当成被删除的结点处理,来修复父结点的下溢情况
6.3.5. 删除黑色叶子结点——删除结点的兄弟结点为红色

如果删除结点的兄弟结点为红色,这样删除结点出现下溢后没办法通过兄弟结点来进行修复。这就需要先把红黑树转换为兄弟结点为黑色的情况,就可以套用上面讲的修复方法来进行修复了。
判定条件:兄弟结点是红色
案例修复:

删除88结点之前,需要先转换成兄弟结点为黑色的情况,当前88的兄弟结点是红色55。可以将其看作LL情况,对父结点88进行右旋转,这样55就被移动上去了,成了80的父结点。76也被移动上去了,成了80的子结点。

这种情况,删除结点88的兄弟结点就变成了黑色,并且没有红色子结点,可以继续套用之前讲的方法来进行修复了。

删除掉88,将80染成黑色,76染成红色,完成修复。
修复步骤总结:
1. 兄弟结点染成 BLACK,父结点染成染成 RED,对父结点进行右旋
2. 于是又回到兄弟结点是黑色的情况(侄子结点变为兄弟结点),继续使用兄弟结点为黑色的方法进行修复
七、红黑树的平衡
AVL是靠平衡因子来保持平衡的,比如平衡因子为1,那么左右子树的高度差就不能超过1,是一种强平衡。
对于红黑树而言,为何那5条性质,就能保证红黑树是平衡的?
因为那5条性质,可以保证红黑树等价于4阶B树。

B树比较矮,它本身就是平衡的,高度越小越平衡。
红黑树就是能保证这个树高度不会特别高,红黑树的最大高度是 2 ∗ log2(n + 1) ,依然是 O(logN) 级别,因为高度不会很大进而维持一种相对平衡的状态。相比AVL树,红黑树的平衡标准比较宽松:没有一条路径会大于其他路径的2倍。这是是一种弱平衡、黑高度平衡(黑高度只算黑色结点个数,红黑树的任何一条路径的黑色结点数一样,则黑高度都是一样)。
八、红黑树的平均时间复杂度
搜索:O(logN)
添加:O(logN),O(1) 次的旋转操作
删除:O(logN),O(1) 次的旋转操作
九、AVL树 vs 红黑树
9.1 AVL树
- 平衡标准比较严格:每个左右子树的高度差不超过1
- 最大高度是 1.44 ∗ log2 n + 2 − 1.328(100W个结点,AVL树最大树高28)
- 搜索、添加、删除都是 O(logN) 复杂度,其中添加仅需 O(1) 次旋转调整、删除最多需要 O(logN) 次旋转调整
9.2 红黑树
- 平衡标准比较宽松:没有一条路径会大于其他路径的2倍
- 最大高度是 2 ∗ log2(n + 1)( 100W个结点,红黑树最大树高40)
- 搜索、添加、删除都是 O(logN) 复杂度,其中添加、删除都仅需 O(1) 次旋转调整
9.3 如何选择
- 搜索的次数远远大于插入和删除,选择AVL树;搜索、插入、删除次数几乎差不多,选择红黑树
- 相对于AVL树来说,红黑树牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于AVL树
- 红黑树的平均统计性能优于AVL树,实际应用中更多选择使用红黑树
9.4 案例对比
10, 35, 47, 11, 5, 57, 39, 14, 27, 26, 84, 75, 63, 41, 37, 24, 96组成一棵树
9.4.1 二叉搜索树

非常不平衡
9.4.2 AVL树

最平衡
9.4.3 红黑树

相对比较平衡
本文介绍红黑树,它是具备某些特性的二叉搜索树,能解决非平衡树问题。文中阐述了红黑树的特性、效率,详细讲解插入和删除操作的各种情况及修复步骤,还对比了红黑树与AVL树,指出搜索多可选AVL树,操作均衡选红黑树。
2539

被折叠的 条评论
为什么被折叠?



