红黑树

可视化:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

平衡二叉树(AVL树)可以保证在最坏的情况下,查找、插入和删除的时间复杂度均为O(logn)。但是插入和删除后重新调整平衡可能需要多大O(logn)次旋转, 频繁地调整平衡导致全树整体拓扑结构地变化。AVL树地左右子树高度差绝对值不超过1,而红黑树在AVL树“适度平衡”的基础上,进一步放宽条件:

红黑树的左右子树高度差不超过两倍。

红黑树也是一种平衡二叉搜索树,虽然和AVL树一样,查找、插入和删除的时间复杂度均为O(logn),但其统计性能更好一些,不需要频繁调整平衡, 任何不平衡都可以在3次旋转之内解决。

因此红黑树被广泛应用,例如 C++ STL set、multiset、map、multimap 都应用了红黑树的变体。

容器 底层结构 是否有序 是否允许重复 查找复杂度
vector 连续数组 ❌ ✔ O(1)
list 双向链表 ❌ ✔ O(n)
deque 分段连续数组 ❌ ✔ O(1)
set 红黑树 ✔ ❌ O(log n)
multiset 红黑树 ✔ ✔ O(log n)
map 红黑树 ✔ ❌ O(log n)
multimap 红黑树 ✔ ✔ O(log n)
unordered_set 哈希表 ❌ ❌ 平均 O(1)
unordered_multiset 哈希表 ❌ ✔ 平均 O(1)
unordered_map 哈希表 ❌ ❌ 平均 O(1)
unordered_multimap 哈希表 ❌ ✔ 平均 O(1)
queue deque(默认) ❌ ✔ ❌(不支持查找)
stack deque(默认) ❌ ✔ ❌(不支持查找)

红黑树的定义

红黑树(red-black tree)是满足以下性质的二叉搜索树。

  1. 每个节点是红色或黑色的
  2. 根节点是黑色的
  3. 每个叶子节点是黑色的
  4. 如果一个节点为红色,其孩子节点必为黑色
  5. 从任一节点到其后代叶子的路径上,均包含相同数目的黑节点。
红黑树

黑高:从某节点x(不包含该节点)到叶子的任意一条路径上黑色节点的个数称为该节点的黑高。

红黑树的黑高:红黑树的黑高为根节点的黑高

红黑树的插入、删除等操作中,必须维护红黑树的5个性质,性质1,3很容易满足,需要特别维护性质2,4,5,即根为黑色,红节点必有黑孩子,左右子树黑高相同。

树高与性能

红黑树没有AVL树那么“平衡”,为什么查找、插入和删除的速度也为O(logn)呢?

这是因为含有n个内部节点的红黑树的高度不超过O(logn)

查找、插入、删除等操作都沿着从根到叶子的路径进行,因此其时间复杂度取决于树的高度。

只需证明:

h=O(logn) h = O(\log n)

其中:

定义:

bh(x)=从节点 x 到叶子路径上的黑色节点数 bh(x) = \text{从节点 } x \text{ 到叶子路径上的黑色节点数}

称为节点 (x) 的 黑高(black-height)。

树高与黑高的关系

由于不存在连续的红色节点, 在最坏情况下,从根到叶子的路径颜色交替出现:

黑→红→黑→红→⋯ \text{黑} \rightarrow \text{红} \rightarrow \text{黑} \rightarrow \text{红} \rightarrow \cdots

因此,路径上红色节点的数量不会超过黑色节点数量。

得到:

h≤2⋅bh(root) h \le 2 \cdot bh(\text{root})

黑高与节点数量的关系

命题: 任意一棵黑高为 (bh) 的红黑树,至少包含:

n≥2bh−1 n \ge 2^{bh} - 1

证明(数学归纳法):

n=0=20−1 n = 0 = 2^0 - 1

2k−1 个节点 2^k - 1 \text{ 个节点}

n≥1+(2k−1)+(2k−1)=2k+1−1 n \ge 1 + (2^k - 1) + (2^k - 1) = 2^{k+1} - 1

命题得证。

推导树高的上界

由上式:

n≥2bh−1 n \ge 2^{bh} - 1

可得:

bh≤log2(n+1) bh \le \log_2 (n + 1)

结合之前得到的不等式:

h≤2⋅bh h \le 2 \cdot bh

代入得:

h≤2log2(n+1) h \le 2 \log_2 (n + 1)

结论

红黑树的高度满足:

h=O(logn) h = O(\log n)

因此,红黑树的基本操作时间复杂度为:

查找=O(logn)插入=O(logn)删除=O(logn) \boxed{ \begin{aligned} \text{查找} &= O(\log n) \ \text{插入} &= O(\log n) \ \text{删除} &= O(\log n) \end{aligned} }

红黑树与4阶B树

红黑树和4阶B树之间存在等价关系,如果从红黑树的树根开始,自顶向下逐层检查,如果遇到红节点,则将该节点压缩到父节点一侧;如果遇到黑节点,则保留。因为红节点对黑高没有贡献,而黑节点对黑高有贡献。

红黑树与4阶B树的4种等价方式。

红黑树与4阶B树的4种等价方式

从红黑树与4阶B树的等价关系可以看出,4阶B树中的节点中必然含有一个黑节点,且最多包含3个关键字;如果包含2个红关键字,则黑关键字必然在中间位置。

为什么就这4种情况,因为这是红黑树的定义决定的。根节点为黑色,如果孩子有一个是黑节点,那么另一个分支黑高应该相同,如图一中的黑黑就满足,如果有一个孩子黑节点,另一个孩子为红节点,那么红节点必须补充两个黑色孩子节点才行,形成了图二和图三的情况,还有一种是两个孩子都是红孩子,有定义叶子节点必须为黑节点,所以红色孩子节点必须有两个黑节点,所以就形成了图四的情况。

不要思考太多,毕竟定义就是这样的。满足定义才能使得树比较平衡。

查找

红黑树本身就是“适度平衡”的二叉搜索树,其查找和二叉搜索树的查找一样。

插入

首先二叉搜索树,插入新节点,肯定是把节点插入到树中作为一个新的叶子节点,然后考虑树的平衡问题。

在红黑树中插入x,首先通过查找,如果查找成功,什么也不做,直接返回。如果查找失败,则在查找失败的位置创建x节点,并置为红色(如果为树根,则置黑色)

为什么插入的新节点一定要置红色?

如果置黑色则有可能改变黑高,违反性质5,如果置红色,不会改变黑高,但可能违反性质4(红色点必然有黑孩子)

插入分两种情况:

  1. 如果新插入节点x的父亲为黑色,则仍然满足红黑树的条件,插入成功
  2. 如果新插入节点x的父亲为红色,则出现双红,此时需要修正,使其满足红黑树的条件。

u为黑色

LL:x及其父亲p出现双红

u为黑色,x及其父亲p出现双红

LR:x为p的右孩子

x为p的右孩子

根据对称性,如果g的左右子树互换位置,则又会出现两种情况(RR型、RL型)。

u为红色

如果x的叔叔u为红色,x及其父亲p出现双红,转为4阶B树,出现上溢,执行分裂操作。

u为红色

根据对称性,其他3种情况,都类似。

红黑树插入修正秘籍

情况 修正方法
p为黑色 满足红黑树性质,无需修正
p为红色,u为黑色 判断g到x的路径为LL、RR、LR、RL执行旋转,旋转后的根染黑,其两个孩子染红
p为红色,u为红色 p、u染黑,g染红,将g看作新插入节点,采取同样的方法处理

插入样例

一列关键字 {12,16,2,30,28,20,60,29,85}

插入样例

删除

注意:如果x节点有左子树和右子树,则实际被删除节点为其直接前驱(或直接后继)。要删除的节点的关键字直接替换为直接前驱(或直接后继)中的关键字,其他啥都不用做,然后把直接前驱(或直接后继)那个节点删掉。

要删除节点x,令r指向实际被删除节点s的接替者,p指向x的父亲,s必有一个孩子为空。

如果实际被删除节点s为红色,直接删除即可;如果s为黑色,则需要根据情况修正,因为黑色节点对黑高有影响,删除一个黑色节点,黑高会减少。

有三种情况

s为红色

删除s后,r接替其位置,满足红黑树的条件(根为黑、无“双红”、黑高不变)。根据红黑树的性质,红节点必有黑孩子,s为红色,其两个孩子必为黑色,s的其中一个孩子为空,另一个孩子也必为空,因为左右子树黑高相等。(下面的是以s的右子树为空为例,左子树为空的情况类似)

红黑树删除情况1 s为红色

s为黑色,接替者r为红色

因为s为黑色,删除s后,黑高减少。又因为p为黑色或红色,接替者r为红色,有可能出现“双红”。 可以直接将r置为黑色,既维护了黑高(删除一个黑色的s,置r为黑色,黑高不变),又避免了“双红”。

下图是s右子树为空情况,左子树为空情况类似。

s为黑色,接替者r为红色

s为黑色,接替者r为黑色

接替者r为黑色,根据左右子树黑高相同原则,r必为空,因为s为黑色,删除s后,黑高减少。 下图,被删除节点及其两个孩子都为黑色,这种情况称为“双黑”,为维护红黑树特性,需要分情况处理。

因为被删节点s为黑色,会产生黑高,因此s必然有兄弟,否则会违反左右子树黑高相同的特性。 将s的兄弟记为 b(brother),s的父亲仍然为p,分为以下4种情况处理。

b为黑色,b有红孩子(BB-1)

首先将红黑树通过压缩转换为4阶B树,删除s后,发生下溢(关键字个数不足), 此时可以向做兄弟借一个关键字,即父亲下沉,左兄弟的最右关键字上移。 而4阶B树的节点中必包含一个黑节点,因此节点t染为黑色,然后将其转换为红黑树即可, 转换后满足红黑树条件(根为黑、无双红、黑高相等)。(长方块表示子树,黑高为0)。

b为黑色,b有红孩子(BB-1)

修正方法:可以看作旋转和染色的过程,p到b的红孩子之间路径为LL,执行右旋,将p右旋,旋转后的根保留原树根的颜色,其两个孩子染黑。

红黑树(修正方法)

同样的道理,如果p到b的红孩子之间路径为LR、则先执行左旋,再执行右旋,最后染色即可

红黑树(修正方法)

根据对称性,如果p的左右子树互换位置,则又会出现两种情况(RR型,RL型),只是旋转不同而已,染色都是一样的。

b为黑色,b无红孩子,p为红色(BB-2-R)

首先将红黑树通过压缩转换为4阶B树,删除s后,发生下溢(关键字个数不足),此时左兄弟关键字只有一个, 不可以借,因此将父亲p下沉,将p的左右子树粘合在一起。由于p为红色,其左侧或右侧必然有一个黑节点,因此p下沉后不再会发生下溢。 将b、p互换颜色,然后将其转换为红黑树即可。

红黑树(修正方法)

为什么将b、p互换颜色,不换色可以吗(旋转不如染色速度快).

红黑树(修正方法)

b为黑色,b红孩子,p为黑色(BB-2B)

首先将红黑树通过压缩转换为4阶B树,删除s后,发生下溢(关键字个数不足),此时左兄弟关键字只有一个,不可以借, 因此将父亲p下沉,将p的左右子树粘合在一起.此时再次发生下溢,粘合后不满足4阶B树的要求(每个节点中有且只有一个黑色), 可以将b染红色,然后将其转换为红黑树. 此事等效为p的父亲被删除,继续做双黑修正,有可能一直向上传递,修正到树根.

红黑树(修正方法)

为什么要将b染为红色?p染为红色可以吗?还是那样,染色比旋转快.

修正方法:该情况修正方法简单 直接,b直接染为红色即可,但一定要注意,不能就这么停止, 根据4阶B树修正原理,此时等效为p的父节点被删除了,继续双黑修正,有可能一直到根.

红黑树(修正方法)

b为红色(BB-3)

首先将红黑树通过压缩转换为4阶B树,删除s后,发生下溢(关键字不足),此时左兄弟关键字个数不确定,若 多于一个则可以向左借,属于BB-1;若只有一个,不可以借,则需要父亲p下沉,属于BB-2-R.
先将b p互换颜色,然后将其转换为红黑树即可.

继续判断属于BB-1或BB-2-R,继续修正.

红黑树(修正方法)

修正方法:

该情况修正首先做右旋(LL),b p直接换色,转换为BB-1(s的兄弟v有红子)或BB-2-R(s的兄弟v无红子,p红) 继续修正.

红黑树(修正方法)

根据对称性,如果p的左右子树互换,则执行左旋(RR),染色,转换为BB-1(s的兄弟v有红子)或BB-2-R

红黑树删除修正秘籍

令r指向实际被删除节点s的接替者,p为s的父亲,b为s的兄弟

情况 修正方法
s红 满足红黑树性质,无需修正
s黑有红子r r染为黑色
s黑无红子 b黑有红子t(BB-1) 判断p到t之间的路径为LL RR LR RL,执行旋转,旋转后根保留原来p的颜色,其两个孩子染为黑色
s黑无红子 b黑无红子p红(BB-2-R) b p换色
s黑无红子 b黑无红子p黑(BB-2-B) b染为红色,此时等效为p的父节点被删除,继续双黑修正
s黑无红子 b红(BB-3) 右旋或左旋,b p换色,转换为BB-1或BB-2-R,继续修正

删除图解

一棵红黑树如下,对其进行一系列的删除操作,展示红黑树删除的所有情况.

删除图解1
  1. 删除85, 85只有右子树,左树为空,且为红色,直接删除令右子树接替即可,满足红黑树条件(根为黑,无双红,黑高不变)
删除85
  1. 删除65,65只有左子树,右子树为空,且为黑色,有红子,直接删除令左子树接替, 红子63染黑,满足红黑树的条件
删除65
  1. 删除75,75的左右子树均有,因此找到75的前驱72(左子树最右节点),令72代替75,删除72即可, 实际被删除节点72为黑色且无红子,出现”双黑” 其兄弟也黑色无红字,父亲为红,符合 BB-2-R 型修正, 删除后,兄弟63和父亲70换色即可.
删除75
  1. 删除78,78为黑色且无红子,出现”双黑”,其兄弟也黑色有红子63,符合BB-1型修正,判断p到t之间的路径为LL,执行右旋,旋转后根保留原p的颜色,其两个孩子染为红色.
删除78
  1. 删除88,88为黑色且无红子,出现”双黑”,其兄弟也黑色无红子,父亲为黑,符合 BB-2-B 型修正,删除88后,兄弟98染红,此时等价为90的父亲(空节点)被删除,再次修正. 该空节点黑色无红孩子,出现”双黑”,其兄弟也黑色无红子,父亲为空,符合 BB-2-R 型修正,删除后 兄弟70 和 父亲 80换色.
删除88
  1. 删除90,90为黑子且有红子,删除后红子染黑即可,满足红黑树的条件
删除90
  1. 删除98,90为黑色且无红子,出现”双黑”,其兄弟为红色,符合BB-3型修正,先右旋,然后兄弟b 父亲p换色,此时转换为 BB-2-R(b无红子,p红),
删除98

BB-2-R型修正,兄弟b 父亲p换色即可,满足红黑树的条件

删除98
  1. 删除60,60左右子树都有,可以令其直接前驱48(左子树最右节点)代替之,然后删除48, 48为黑色且无红子,出现”双黑”, 其兄弟也黑色无红子,父亲为黑,符合 BB-2-B 型修正,48删除后,兄弟25 染红,此时等价为30的父亲(空节点)被删除,再次修正
删除60

该空节点黑色无红孩子,出现”双黑”,其兄弟也黑色无红子,父亲为黑,符合 BB-2-B型修正,删除后,兄弟70染色即可,满足红黑树的条件,此时被删除节点的父亲的父亲不存在,迭代停止.

删除60