数据结构之红黑树

14.红黑树

1.定义:

红黑树是一种自平衡的二叉查找树,是一种高效的查找树。它是由 Rudolf Bayer 于1978年发明,在当时被称为平衡二叉 B 树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的红黑树。红黑树具有良好的效率,它可在 O(logN) 时间内完成查找、增加、删除等操作。

红黑树具备了某些特性的二叉搜索树,能解决非平衡树问题,红黑树是一种接近平衡的二叉树(说它是接近平衡因为它并没有像AVL树的平衡因子的概念,它只是靠着满足红黑节点的5条性质来维持一种接近平衡的结构,进而提升整体的性能,并没有严格的卡定某个平衡因子来维持绝对平衡)

2.特性

在讲解红黑树性质之前,先简单了解一下几个概念:

  • parent:父节点
  • sibling:兄弟节点
  • uncle:叔父节点( parent 的兄弟节点)
  • grand:祖父节点( parent 的父节点)

红黑树同时满足以下特性:

  1. 节点不是黑色,就是红色(非黑即红)
  2. 根节点为黑色
  3. 叶节点为黑色(叶节点是指末梢的空节点 Nil或Null)
  4. 一个节点为红色,则其两个子节点必须是黑色的(根到叶子的所有路径,不可能存在两个连续的红色节点)
  5. 每个节点到叶子节点的所有路径,都包含相同数目的黑色节点(相同的黑色高度)
  • 约束4和5,保证了红黑树的大致平衡:根到叶子的所有路径中,最长路径不会超过最短路径的2倍。
  • 使得红黑树在最坏的情况下,也能有O(log2N)的查找效率
    • 黑色高度为3时,最短路径:黑色→ \rightarrow→ 黑色 → \rightarrow→ 黑色,最长路径:黑色→ \rightarrow→ 红色 → \rightarrow→ 黑色 → \rightarrow→ 红色 → \rightarrow→ 黑色
    • 最短路径的长度为2(不算Nil的叶子节点),最长路径为4
  • 关于叶子节点:Java实现中,null代表空节点,无法看到黑色的空节点,反而能看到传统的红色叶子节点
  • 默认新插入的节点为红色:因为父节点为黑色的概率较大,插入新节点为红色,可以避免颜色冲突

3.红黑树的效率

红黑树的查找,插入和删除操作,时间复杂度都是O(logN)

4.红黑树和AVL树的比较

  • AVL树的时间复杂度虽然优于红黑树,但是对于现在的计算机,cpu太快,可以忽略性能差异
  • 红黑树的插入删除比AVL树更便于控制操作
  • 红黑树整体性能略优于AVL树(红黑树旋转情况少于AVL树)

5.红黑树的等价转换

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

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

由上面的等价变换我们就可以得到如下结论:

  1. 红黑树 和 4阶B树(2-3-4树)具有等价性
  2. 黑色节点与它的红色子节点融合在一起,形成1个B树节点
  3. 红黑树的黑色节点个数 与 4阶B树的节点总个数相等
  4. 在所有的B树节点中,永远是黑色节点是父节点,红色节点是子节点。黑色节点在中间,红色节点在两边。

5.红黑树的左右旋转

可以理解为提着旋转另一侧的孩子节点(左旋提有孩子,右旋提左孩子),将整个树提起来

右旋节点 M 的步骤如下:

  1. 将节点 M 的左孩子引用指向节点 E 的右孩子
  2. 将节点 E 的右孩子引用指向节点 M,完成旋转

以下了解即可

6.插入

默认插入为红色节点

红黑树插入要分情况来看

1.满足红黑树的性质 4

有 4 种情况满足红黑树的性质 4 :parent 为黑色节点。这四种情况不需要做任何额外的处理。

2.不满足性质4,LL RR

RR****情况:父节点为祖父节点的右节点,插入节点为父节点的右节点

LL情况:父节点为祖父节点的左节点,插入节点为父节点的左节点

**判定条件:**uncle 不是红色节点。

  1. parent 染成黑色,grand 染成红色
  2. grand 进行单旋操作
    1. LL:右旋转
    2. RR:左旋转

3.不满足性质4,LR RL

RL****情况:父节点为祖父节点的右节点,插入节点为父节点的左节点

LR情况:父节点为祖父节点的左节点,插入节点为父节点的右节点

**判定条件:**uncle 不是红色节点

  1. 插入节点染成黑色,grand 染成红色
  2. 进行双旋操作
    • LR:parent 左旋转, grand 右旋转
    • RL:parent 右旋转, grand 左旋转

3.上溢的LL插入情况

超出了4阶B树节点的容量范围,这种情况称为上溢

上溢LL情况:父节点为祖父节点的左节点,插入节点为父节点的左节点。并且构成的新的B树节点已经超过了B树节点容量大小范围。

**判定条件:**uncle 是红色节点。满足这个条件的就都是上溢的情况,上溢的修复只需要染色,不需要旋转。

  1. parent、uncle 染成黑色
  2. grand 向上合并
    1. 将向上合并的grand染成红色,相对上一层,就当做是新添加的节点,再次来一遍插入情况的判断,进行处理。

4.上溢的RR插入情况

上溢RR情况:父节点为祖父节点的右节点,插入节点为父节点的右节点。并且构成的新的B树节点已经超过了B树节点容量大小范围。

**判定条件:**uncle 是红色节点

  1. parent、uncle 染成黑色
  2. grand 向上合并
    • 染成红色(其实染成红色就已经是完成了向上合并,因为祖父节点和祖父节点的父节点的连接指向并没有变),当做是新添加的节点进行处理

5.上溢的LR插入情况

上溢LR情况:父节点为祖父节点的左节点,插入节点为父节点的右节点。并且构成的新的B树节点已经超过了B树节点容量大小范围。

**判定条件:**uncle 是红色节点

  1. parent、uncle 染成黑色
  2. grand 向上合并
    • 染成红色,当做是新添加的节点进行处理

6.上溢的RL插入情况

上溢RL情况:父节点为祖父节点的右节点,插入节点为父节点的左节点。并且构成的新的B树节点已经超过了B树节点容量大小范围。

**判定条件:**uncle 是红色节点

  1. parent、uncle 染成黑色
  2. grand 向上合并
    • 染成黑色,当做是新添加的节点进行处理

7.删除

B树中,最后真正被删除的元素都在叶子节点中。所以在红黑树中,被删除的节点一定也在最后一层。

1.删除红色节点

直接删

2.删除拥有1个红色子节点的黑色节点

删除拥有1个红色子节点的黑色节点的情况,是需要我们做相关的处理的。

对于一个二叉树来说,删除一个度为1的节点(度指的是一个节点的子节点个数),将其删除后需要用它唯一的子节点来进行替换。而红黑树的这种情况的判定条件,就是判定要替代删除节点的子节点是不是红色

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

修复方法

  1. 用删除节点的唯一子节点对其进行替代
  2. 将替代节点染成黑色

3.删除黑色叶子节点------删除节点为根节点

一棵红黑树只有一个黑色根节点(也就是唯一的一个叶子节点,整个红黑树只有这一个黑色节点),可直接删除该节点,无需做其他操作。

4.删除黑色叶子节点------删除节点的兄弟节点为黑色

因为四阶B树的节点中最少存有1个元素,如果不足,则会造成下溢。也就是需要从兄弟节点中借一个子节点出来。

1.兄弟节点至少有1个红色子节点

黑色叶子节点被删除后,会导致B树节点下溢(比如删除88),就可以从兄弟节点中借出一个红色子节点来进行修复。

**判定条件:**兄弟节点至少有 1 个红色子节点

修复方法:

  1. 进行旋转操作
  2. 旋转之后的中心节点继承父节点(删除节点的父节点)的颜色
  3. 旋转之后的左右节点染为黑色
2.兄弟节点没有红色子节点

当删除节点的兄弟节点没有红色节点可以借出的情况下,就需要父节点来向下合并进行修复,父节点向下和兄弟节点合并成新的B树节点来解决下溢。

**判定条件:**兄弟节点没有1个红色子节点

修复步骤总结:

  1. 父节点向下与兄弟节点进行合并
  2. 将兄弟染成红色、父节点染成黑色即可修复红黑树性质
    • 如果父节点是黑色,直接将父节点当成被删除的节点处理,来修复父节点的下溢情况

5. 删除黑色叶子节点------删除节点的兄弟节点为红色

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

修复步骤总结:

  1. 兄弟节点染成 BLACK,父节点染成染成 RED,对父节点进行右旋
  2. 于是又回到兄弟节点是黑色的情况(侄子节点变为兄弟节点),继续使用兄弟节点为黑色的方法进行修复
相关推荐
Envyᥫᩣ9 分钟前
C#语言:从入门到精通
开发语言·c#
狂放不羁霸25 分钟前
idea | 搭建 SpringBoot 项目之配置 Maven
spring boot·maven·intellij-idea
九圣残炎26 分钟前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode
wclass-zhengge28 分钟前
Netty篇(入门编程)
java·linux·服务器
童先生30 分钟前
Go 项目中实现类似 Java Shiro 的权限控制中间件?
开发语言·go
lulu_gh_yu31 分钟前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
Re.不晚1 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
老秦包你会1 小时前
Qt第三课 ----------容器类控件
开发语言·qt
凤枭香1 小时前
Python OpenCV 傅里叶变换
开发语言·图像处理·python·opencv
雷神乐乐1 小时前
Maven学习——创建Maven的Java和Web工程,并运行在Tomcat上
java·maven