[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树

文章目录

      • 0. 前言
      • 1. 红黑树的概念
        • 1.1 疑问:红黑树如何保持平衡呢?
        • 1.2 常见的红黑树
      • 2. 红黑树的构成性质
        • 2.1 红黑树的5条重要性质
        • 2.2 请问下面这棵树是 红黑树 吗?
      • 3. 红黑树与4阶B树的关系
        • 3.1 红黑树与4阶B树的等价变换
        • 3.2 红黑树 VS 2-3-4树
      • 4. 相关英文单词
      • 5. 红黑树的添加
        • 5.1 添加前准备
        • 5.2 添加的所有情况
        • 5.3 修复性质4--LL\RR
        • 5.4 修复性质4--LR\RL
        • 5.5 如何区分其它的四种情况
        • 5.6 修复性质4 – 上溢 – LL
        • 5.7 修复性质4 – 上溢 – RR
        • 5.8 修复性质4 – 上溢 – LR
        • 5.9 修复性质4 – 上溢 – RL
      • 6. 红黑树添加元素总结
      • 7. 红黑树的删除
        • 7.1 删除 – RED节点
        • 7.2 删除 – BLACK节点
        • 7.3 删除 – 拥有1个RED子节点的BLACK节点
        • 7.4 删除 – BLACK叶子节点 – sibling(兄弟节点)为BLACK,且有 RED 子节点
        • 7.5 删除 – BLACK叶子节点 – sibling(兄弟节点)为BLACK,没有 RED 子节点
        • 7.6 删除 – BLACK叶子节点 – sibling(兄弟节点)为RED
      • 8. 红黑树删除元素总结
      • 9. 答疑:红黑树的平衡
      • 10. 平均时间复杂度
      • 11. AVL树 vs 红黑树

0. 前言

在学习红黑树前,个人建议先学习下 B 树,尤其需要了解 4 阶 B 树的添加、删除,在本博文后续讲解中会大量使用,可参考[C++ 系列] 79. 详解B树搜索、添加、删除

1. 红黑树的概念

红黑树也是一种自平衡二叉搜索树,以前也叫做平衡二叉 B 树 ( Stmmertic Binary B-tree )。

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩
倍**,因而是接近平衡的。
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第1张图片

1.1 疑问:红黑树如何保持平衡呢?

红黑树没有 AVL 树中 平衡因子 的概念,那么它是如何保持平衡的呢?

  • 在下面学习完红黑树插入、删除再进行解答。

1.2 常见的红黑树

一颗标标准准的 红黑树 结构如下图所示:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第2张图片

2. 红黑树的构成性质

2.1 红黑树的5条重要性质

若一颗树是 红黑树 的话一定要满足如下 5 条特点:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第3张图片
在这的叶子节点是想象出来的 空节点,即上图的 null 黑色 节点并不为上图的25、74、78、86、90这几个节点。

2.2 请问下面这棵树是 红黑树 吗?

[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第4张图片
眨眼一看好像满足红黑树的各项性质,所以节点仅有红、黑两种颜色,且根节点为黑,任一路径上没有两个相邻的红色节点,并且从根节点到达叶子节点的黑色数量都为 3,它好像就是一颗红黑树?

在这的叶子节点是想象出来的 空节点,即上图的 null 黑色 节点,故能够发现:55–>38–>null(到达叶子节点) 仅经过了 2 个黑色节点,二其它线路都为 3 个黑色节点,故其不为 红黑树。

网上技术博文大批量的犯这个错误…在此特别之处谨记。
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第5张图片

3. 红黑树与4阶B树的关系

3.1 红黑树与4阶B树的等价变换

将下图左边的 红黑树 红色节点上升一层得到右边的这棵树?

仔细观察下,右边这棵树不就是颗 B 树吗?并且还是个 4 阶 B 树,将其转换为更加形象的样子,见右下图:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第6张图片

  • 红黑树 和 4 阶 B 树(2-3-4树)具有等价性
  • BLACK 节点与它的 RED 子节点融合在一起,形成1个B树节点
  • 红黑树的 BLACK 节点个数 与 4阶B树的节点总个数相等
  • 网上有些教程:用 2-3树 与 红黑树 进行类比,这是极其不严谨的,2-3树 并不能完美匹配 红黑树 的所有情况

注意:后面展示的红黑树都会省略 NULL 节点

3.2 红黑树 VS 2-3-4树

[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第7张图片
如果上图最底层的 BLACK 节点是不存在的,则整颗 B 树只有 1 个节点,而且是超级节点。

4. 相关英文单词

[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第8张图片
17 就是 33 的兄弟节点,共同的父节点为 25。
17 与 33 的叔父节点就是 46,50 的叔父节点就是 25。
17、33、50 的祖父节点为 38。

5. 红黑树的添加

5.1 添加前准备

现在看到红黑树一定要做到心中有4阶 B 树,会很大方便理解,并且下面的讲解也是依据 B 树的转化进行。

已知:

  • B树中,新元素必定是添加到叶子节点中
  • 4 阶 B 树所有节点的元素个数 x 都符合 1 ≤ x ≤ 3
  • 建议新添加的节点默认为 RED,这样能够让红黑树的性质尽快满足(性质 1、2、3、5 都满足,性质 4 不一定)
  • 如果添加的是根节点,染成 BLACK 即可

[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第9张图片

5.2 添加的所有情况

[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第10张图片
添加新节点只可能加到最后一层的叶子节点上,那么最后一层的叶子节点有多少种情况呢?

其实上图已经给出了所有情况:黑、红黑、黑红、红黑红,由于是 4 阶 B 树,故出现且仅出现这些情况。

那么在这些叶子节点中进行插入,排除一些不能插入的错误情况类似于 25 虽然在 B 树中为叶子节点,但在 红黑树 概念中其有左右孩子了,故将其排除掉。同理可排除其他不可插入情况,得到如下的 12 种插入情况:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第11张图片
其中有 4 种情况满足红黑树的性质 4,其父节点 BLACK,同样也满足 4 阶 B 树的性质,因此不需要做任何额外处理,见下图所示:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第12张图片
其中有 8 种情况不满足红黑树的性质 4:parent 为 RED(Double Red),其中前 4 种属于 B 树节点上溢的情况,见下图所示:[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第13张图片

5.3 修复性质4–LL\RR

其它 6 种情况暂且不论,先看看下图这两种情况:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第14张图片
即 50–>52、72–>60 违背了性质四。首先站在 B 树的角度来思考,52、60 加入必然要成为一个 B 树节点,那么 回想一下 B 树节点是怎么形成的呢?B 树节点是由一个黑色节点与他的红色子节点形成的, 那么在此若 52 想成为 B 树节点,那么 50 就需要变成黑色,并且 46 变成 红色,才能合并到一起变为 B 树节点。

此时会发现,红38–>红46 岂不是两个红色相连了?在这的直观思想是让 红38–>黑50,不就能解决了吗?再仔细观察,这是不是 AVL 树中的 RR 情况,针对 46 进行左旋转,让 50 成为子树的根节点,46 成为子树的子节点,就万事大吉了!

同理,将 72 染成黑色成为 B 树节点,76 需要染成红色,并且现在是 LL 情况,仅需对 76 进行右旋转,让 72 成为子树的根节点,76 成为 72 的子节点。

转换效果图如下图所示:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第15张图片
判定条件:uncle 不是 RED

  1. parent 染成 BLACK,grand 染成 RED
  2. grand 进行单旋操作

LL:grand 右旋转
RR:grand 左旋转

5.4 修复性质4–LR\RL

上述两种情况搞定后,再看看下图这两种情况:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第16张图片
有了上面的基础可以知道,46–>50–>48是 RL 情况,76–>72–>74 是 LR 情况。

那么针对46–>50–>48是 RL 情况,就先让 50 做右旋转,再让 46 做左旋转,现在 48 上去成为这颗子树的根节点,那么就需要对 48 将其染为 黑色,左右变成红色。

转换效果图如下图所示:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第17张图片
判定条件:uncle 不是 RED

  1. 自己染成 BLACK,grand 染成 RED
  2. 进行双旋操作

LR:parent 左旋转, grand 右旋转
RL:parent 右旋转, grand 左旋转

5.5 如何区分其它的四种情况

判断 uncle 颜色是否为 RED,如果不为 RED 就是上面四种情况,如果为 RED 则为下述 4 种情况。

下面举几个 uncle 的例子,红黑树 如下图所示:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第18张图片

  • 10 的 uncle 节点 是 33,为红色
  • 48、52 的 uncle 节点是 46 的左孩子为 null,可将 null 视为黑色,故其 uncle 为黑色
  • 60、74 的 uncle 节点是 76 的右孩子为 null,可将 null 视为黑色,故其 uncle 为黑色

5.6 修复性质4 – 上溢 – LL

经过上面情况的区分,来看一种新的情况,上溢 – LL,如下图所示:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第19张图片
首先从 B 树角度出发,25、17、33、10 明显产生了上溢情况,因为 4 阶 B 树最多只能存 3 个数据,现在产生了上溢。

解决方法就是跳出最中间的向上合并,然后进行分裂即可。在这 17、25 都算是中间的元素,在这就挑 25 作为中间节点向上合并,因为其颜色刚好搭配,在这不要思考 25 向上合并后的各种情况,比如 38 作为 B 树根节点一定需要变为黑色,而相邻节点变为红色等系列的问题,先来考虑 17–>10、33 小方面的针对 B 树情况进行考虑。

17、33 由于 25 的向上合并,都要变为 B 树节点,在这就需要将其变为黑色。

那么一个小总结就出来了,若是 上溢 – LL 情况,即需要将其父节点、叔父节点均变成黑色即可。

最后来考虑祖父节点 25 的向上合并,本来 38、55 这个位置只有两个节点,现在 25 向上合并,就相当于对 38、55 进行新添加节点 25 的操作,那么就是依然是插入的 12 种情况,将其变成红色配对情况即可,这是一个递归的操作!效果如下图所示:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第20张图片
但若祖父节点 25 持续上溢到根节点的话,并且此时根节点也触发了上溢的条件的话,即下图的情况。此时又需要找中间的元素,在下图中就是 38、55 这两个情况,只需要将根节点转成黑色即可。
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第21张图片
这个上溢的 LL 情况,只需要进行染色即可,根本没进行旋转的操作。

判定条件:uncle 是 RED

  1. parent、uncle 染成 BLACK
  2. grand 向上合并,并染成 RED,当做是新添加的节点进行处理
  3. grand 向上合并时,可能继续发生上溢,若上溢持续到根节点,只需将根节点染成 BLACK 即可。

5.7 修复性质4 – 上溢 – RR

顺理成章的又出来了一种新的情况,上溢 – RR,如下图所示:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第22张图片
有了上面 5.6 的经验,现在的操作就简单很多了。将 36 的祖父节点 25 拿出来染成红色,当做一个新的节点,实行插入操作的向上合并。将 17、33 的父节点、叔父节点染成黑色,此时 17、33 就独立成了一个 B 树节点,其它的都与之前的逻辑一致了。直接看效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第23张图片
判定条件:uncle 是 RED

  1. parent、uncle 染成 BLACK
  2. grand 向上合并,并染成 RED,当做是新添加的节点进行处理,可能继续发生上溢,若上溢持续到根节点,只需将根节点染成 BLACK 即可。

5.8 修复性质4 – 上溢 – LR

倒数第二种情况了,上溢 – LR,如下图所示
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第24张图片
此时 20、17、25、33 依然发生上溢,同理祖父节点 25 需要进行向上合并,叔父节点染成黑色即可。效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第25张图片
判定条件:uncle 是 RED

  1. parent、uncle 染成 BLACK
  2. grand 向上合并,并染成 RED,当做是新添加的节点进行处理,可能继续发生上溢,若上溢持续到根节点,只需将根节点染成 BLACK 即可。

5.9 修复性质4 – 上溢 – RL

最后一种情况,上溢 – RL,如下图所示:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第26张图片
现在的话道理都是一样的了,直接效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第27张图片
判定条件:uncle 是 RED

  1. parent、uncle 染成 BLACK
  2. grand 向上合并,并染成 RED,当做是新添加的节点进行处理,可能继续发生上溢,若上溢持续到根节点,只需将根节点染成 BLACK 即可。

6. 红黑树添加元素总结

站在 4 阶 B 树的角度上看,红黑树添加共有 12 种情况,其中 4 种情况符合红黑树定义,不需要进行修复调整,而其余八中情况可分为如下三大情况:

  • 若插入节点位置 uncle 不为 RED 的 LL\RR 情况的话,仅需要 parent 染成 BLACK,grand 染成 RED 并将grand 进行对应的单旋操作即可
    LL:grand 右旋转
    RR:grand 左旋转
  • 自己染成 BLACK,grand 染成 RED 进行双旋操作即可
    LR:parent 左旋转, grand 右旋转
    RL:parent 右旋转, grand 左旋转
  • 若插入节点位置 uncle 为 RED 情况的话,就为上溢的情况,不需要进行旋转,仅染色和向上合并即可。具体为 parent、uncle 染成 BLACK,grand 向上合并,并染成 RED,当做是新添加的节点进行处理,可能继续发生上溢,若上溢持续到根节点,只需将根节点染成 BLACK 即可。

7. 红黑树的删除

同样对比 4 阶 B 树我们知道,在 B 树中真正从内存中被删除的节点元素都在叶子节点中。那么就有下面几种情况。

7.1 删除 – RED节点

红黑树结构图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第28张图片
假如删除红色 17、33、50、72 删除一个或多个的话,对红黑树没有影响,它依然是一颗 红黑树,并且是一颗 4 阶 B 树。

7.2 删除 – BLACK节点

红黑树结构图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第29张图片
有 3 种情况:

  1. 拥有 2 个 RED 子节点的 BLACK 节点,在此即 25 节点。25 节点的度为 2, 而在二叉搜索树当中,一定是不可能删除一个度为 2 的节点的,同时一定会找到它的 前驱 / 后继 进行删除。不可能被直接删除,因为会找它的子节点替代删除。因此不用考虑这种情况

  2. 拥有 1 个 RED 子节点的 BLACK 节点,在此即 46、76 节点

  3. BLACK 叶子节点,在此即 88 节点

因为情况 1 不必考虑,那么删除情况就仅有拥有1个RED子节点的BLACK节点、BLACK 叶子节点这两种情况了。

7.3 删除 – 拥有1个RED子节点的BLACK节点

红黑树结构如下图:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第30张图片
加入现在要删除 46 、76 的情况,那怎么判定是这种情况呢?仔细观察可发现,它是度为 1 的节点,在二叉搜索树中,就需要找到他的子节点进行删除即可,即将 46 删除 并将 55–>50,将 76 删除 并将 80–>72,即效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第31张图片
即为:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第32张图片
这样删除明显不符合红黑树的性质啊,如果从 B 树角度出发,将 46、76 进行直接删除,那么 50、72 独立成为 B 树节点,那么 B 树节点一定是黑色的。

判定条件:用以替代的子节点是 RED

  • 将替代的子节点染成 BLACK 即可保持红黑树性质

7.4 删除 – BLACK叶子节点 – sibling(兄弟节点)为BLACK,且有 RED 子节点

红黑树结构图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第33张图片
在这中情况下就是删除 46、88 节点,在 BST 树角度上,就是直接被删除,但在 4 阶 B 树中就发生了下溢,因为 4 阶 B 树保证节点的数据最少要有 1 个,最多为 3 个。在这进行了 88 的删除的话,自然发生下溢情况。

在 B 树中处理下溢情况的方法就是,看看兄弟节点是否能借出一个节点在这里先考虑能借出节点的三种情况如下图:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第34张图片
通过观察能够发现这三种情况下,它的兄弟节点都有一个红色孩子节点,这样兄弟节点就至少有两个节点了,才能借出一个节点。并且兄弟节点的颜色一定是黑色,如果它的兄弟节点为红色的话,那么这个兄弟节点一定是在 父节点 里面的。我们能够知道如果父节点为红色,那么兄弟节点就不能再为红色,即为黑色,如果父节点为黑色,根据红黑树与 4 阶 B 树等价原则,父节点与他的所有子节点构成一个 B 树节点,那么在 B 树中这个红色兄弟节点是在父节点上的。

就如下如图所示,55 作为父节点怎么可能把节点借出来呢?我们需要借的是左右临近的红色节点。
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第35张图片
现在见上图,如果删掉 88,76 兄弟给借红色子节点 78 来处理下溢问题,那么就是 80 作为父节点下去到 88 的位置,在兄弟里面挑一个,尽量挑个居中的作为父节点,即 78 上去作为新的父节点
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第36张图片并且还能够发现上图这个情况其实是从 80–>76–>78 d的 LR 情况,首先对 76 进行左旋转,那么 78 就上去了,再对 78 做右旋转 那么 78 就上去了,80 就下去了,下溢情况调整完毕。

通过旋转之后新的父节点要继承原有父节点的颜色,并且它的左右节点需要变成独立的 B 树节点,那么就需要将其左右节点全部染成黑色。因为 80 旋转下去就它一个,肯定为独立的 B 树节点,76 作为它的兄弟节点也一定为 黑色 并且也是独立的 B 树节点。

到现在,删除 – BLACK叶子节点 – sibling(兄弟节点)为BLACK 的情况这样才算调整完毕,接下来再看几个例子加深理解:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第37张图片
删除 88 节点,现在就是很明显的从 80–>76–>72 的 LL 情况,直接对 80 节点进行右旋转即可。并且旋转过后,76 继承 80 的颜色,并将 76 的左右节点染成黑色。效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第38张图片
再来个例子:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第39张图片
删除 88 节点,现在就是很明显的从 80–>76–>72 的 LL 情况,从 80–>76–>78 的 LR 情况,在这就选择 LL 对 直接对 80 节点进行右旋转即可,因为这样只需要旋转一次,而 LR 却要进行两次旋转。并且旋转过后,76 继承 80 的颜色,并将 76 的左右节点染成黑色。效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第40张图片
如果按照 LR 来进行两步旋转的话,结构和上面的又不太一致,因为一开始对 76 进行左旋转,78 上去,再对 78 进行右旋转,78 作为新的父节点,80 下去解决下溢问题,再将 78 染成原来的父节点颜色,左右染成黑色即可,效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第41张图片
所以红黑树的删除,删完之后可能出现多种树的结构,可能都是满足情况的正确答案。

判定条件:BLACK 叶子节点被删除后,会导致B树节点下溢(比如删除88),此时如果 sibling 至少有 1 个 RED 子节点

  • 进行旋转操作
  • 旋转之后的中心节点继承 parent 的颜色
  • 旋转之后的左右节点染为 BLACK

7.5 删除 – BLACK叶子节点 – sibling(兄弟节点)为BLACK,没有 RED 子节点

红黑树效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第42张图片
依旧删除 88,此时它的兄弟节点没有红色子节点,发生了下溢的情况,那么就需要用到 B 树的 合并 方式来处理下溢的情况了。合并是从父节点 80 下来和 76 合并到一起,那么父节点下来与它的子节点融合到一起成为 B 树节点,此时就需要对父节点染成黑色,将兄弟节点染成红色即可。效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第43张图片
如果父节点是黑色的情况呢?如果父节点下来,B 树节点必然产生下溢情况。那么要是父节点旁边出现红色节点呢?是不是就不会产生下溢情况了?

答案是不可能再出现红色节点的,因为删除节点为黑色叶子节点,并且它的兄弟节点仍是黑色节点,此时父节点的 left、right 都已经有所指了,自然不会存在再指向一个红色节点的情况。如下图:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第44张图片
其实这个情况处理很简单,只需要将父节点当做被删除的节点即可,也是一个递归的调用。很类似于处理上溢问题的递归调用。
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第45张图片
判定条件:sibling 没有 1 个 RED 子节点

  • 将 sibling 染成 RED、parent 染成 BLACK 即可修复红黑树性质
  • 如果 parent 是 BLACK,会导致 parent 也下溢,这时只需要把 parent 当做被删除的节点处理即可

7.6 删除 – BLACK叶子节点 – sibling(兄弟节点)为RED

在此黑色的叶子节点的两种情况已经讲完了,下面来看最后一种情况,红黑树结构图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第46张图片
依旧删除 88,在 B 树中就产生了下溢的情况,还得看 76 能不能借节点,如果能借就借一个节点,如果不能借就让父节点下来合并解决下溢问题。但是在这就比较麻烦,现在 88 的兄弟是 55 ,55 的儿子才是 76,就多跨了一层关系,现在为了解决这个问题,我们强制让 76 做 88 的兄弟,那么就又回到了黑兄弟的情况了。
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第47张图片
那么思想就很简单了,就是单纯的让 80 指向 76 就好了。见上图可知道可以通过 LL 情况实现,让 80 进行右旋转,此时 55 上去成为新的父节点,80 指向 55 的右孩子 76 节点,而 55 的右边指向 80 。再将删除节点的兄弟节点即 55 节点染成黑色,父节点染成红色即可,效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第48张图片
于是又回到了兄弟节点为黑色的情况,即 7.5 的情况。如果将 88 删掉,产生下溢,父节点向下合并,成为 B 树节点,即将 80 染黑,76 染红即可,效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第49张图片
再来举一个例子:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第50张图片
依旧删除 88 节点,需要让 80 的左边指向 76,并将父节点染成红色,兄弟节点染成黑色:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第51张图片
回到兄弟节点为黑色的情况了,按照 80–>76–>78 进行 LR 两步旋转的话,一开始对 76 进行左旋转,78 上去,再对 78 进行右旋转,78 作为新的父节点,80 下去解决下溢问题,再将 78 染成原来的父节点颜色,左右染成黑色即可,效果图如下:
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第52张图片
判定条件:sibling 是 RED

  • sibling 染成 BLACK,parent 染成 RED,进行旋转
  • 于是又回到 sibling 是 BLACK 的情况

8. 红黑树删除元素总结

与 BST 树一致真正删除的节点肯定是删除的叶子节点,那么以待删除节点的颜色来考虑,若为红色,则直接删除即可,不需要作任何调整,若为黑色,则分以下三种情况:

  1. 拥有 2 个 RED 子节点的 BLACK 节点,不可能被直接删除,因为会找它的子节点替代删除。因此不用考虑这种情况

  2. 拥有 1 个 RED 子节点的 BLACK 节点

  3. BLACK 叶子节点

于是就只有情况2、3 需要我们来讨论,即有:

  1. 删除 – 拥有 1 个 RED 子节点的 BLACK 节点
  • 判定条件:用以替代的子节点是 RED
  • 解决方式:将替代的子节点染成 BLACK 即可保持红黑树性质
  1. 删除 – BLACK 叶子节点 – sibling为 BLACK
  • 判定条件:BLACK 叶子节点被删除后,会导致B树节点下溢(比如删除88),并且如果 sibling 至少有 1 个 RED 子节点
  • 解决方式:进行旋转操作,旋转之后的中心节点继承 parent 的颜色,旋转之后的左右节点染为 BLACK
  1. 删除 – BLACK 叶子节点 – sibling为 BLACK
  • 判定条件:sibling 没有 1 个 RED 子节点
  • 解决方式: 将 sibling 染成 RED、parent 染成 BLACK 即可修复红黑树性质,处理后如果 parent 是 BLACK会导致 parent 也下溢,这时只需要把 parent 当做被删除的节点调用递归的删除即可
  1. 删除 – BLACK叶子节点 – sibling为RED
  • 判定条件:sibling 是 RED
  • 解决方式: 将 sibling 染成 BLACK,parent 染成 RED 进行旋转,于是又回到 sibling 是 BLACK 的情况

9. 答疑:红黑树的平衡

最初遗留的困惑:为何那5条性质,就能保证红黑树是平衡的?
回答:那5条性质,可以保证 红黑树 等价于 4阶B树,在这就不证明了,有兴趣coder可自行证明,或者查阅相关资料
[C++ 系列] 79. 基于4阶B树详解R-BTree红黑树_第53张图片

  • 相比AVL树,红黑树的平衡标准比较宽松:没有一条路径会大于其他路径的2倍,因为要保证每条路径下黑色节点个数一致,那么最短路径就是全黑节点,最长路径就是一个黑紧接着一个红,这样最长路径的节点个数也就只能不超过最短路径的节点个数了
  • 是一种弱平衡、黑高度平衡
  • 红黑树的最大高度是 2 ∗ l o g ( N + 1 ) 2 ∗log(N+1) 2log(N+1),依然是 O ( l o g N ) O(logN) O(logN) 级别

10. 平均时间复杂度

◼ 搜索: O ( l o g N ) O(logN) O(logN)

◼ 添加: O ( l o g N ) O(logN) O(logN) O ( 1 ) O(1) O(1) 次的旋转操作

◼ 删除: O ( l o g N ) O(log N) O(logN) O ( 1 ) O(1) O(1) 次的旋转操作

11. AVL树 vs 红黑树

AVL树:

  • 平衡标准比较严格:每个左右子树的高度差不超过1
  • 最大高度是 1.44 ∗ l o g 2 N + 2 − 1.328 1.44 ∗ log_2 N + 2 − 1.328 1.44log2N+21.328(100W个节点,AVL 树最大树高28)
  • 搜索、添加、删除都是 O ( l o g 2 N ) O(log_2N) O(log2N) 复杂度,其中添加仅需 O ( 1 ) O(1) O(1) 次旋转调整、删除最多需要 O ( l o g 2 N ) O(log_2N) O(log2N) 次旋转调整

红黑树:

  • 平衡标准比较宽松:没有一条路径会大于其他路径的2倍

  • 最大高度是 2 ∗ l o g 2 ( n + 1 ) 2 ∗ log_2(n + 1) 2log2(n+1)( 100W个节点,红黑树最大树高40)

  • 搜索、添加、删除都是 O ( l o g 2 N ) O(log_2 N ) O(log2N)复杂度,其中添加、删除都仅需 O ( 1 ) O(1) O(1)次旋转调整

  • 搜索的次数远远大于插入和删除,选择 AVL 树;搜索、插入、删除次数几乎差不多,选择红黑树

  • 相对于 AVL树来说,红黑树牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于 AVL 树

  • 红黑树的平均统计性能优于 AVL 树,实际应用中更多选择使用红黑树

你可能感兴趣的:([C++系列],C++,B树,红黑树)