一、为什么要发明红黑树?
平衡二叉树AVL:插入/删除很容易破坏“平衡”特性,需要频繁调整树的形态。如:插入操作导致不平衡,则需要先计算平衡因子,找到最小不平衡子树(时间开销大),再进行LL/RR/LR/RL调整;
红黑树RBT:插入/删除很多时候不会破坏“红黑”特性,无需频繁调整树的形态。即便需要调整,一般都可以在常数级时间内完成;
平衡二叉树:适用于以查为主、很少插入/删除的场景;
红黑树:适用于频繁插入、删除的场景,实用性更强;
二、红黑树大概会怎么考?
红黑树的定义、性质–选择题;
猜测:红黑树的考察形式会和平衡二叉树以往的考察形式类似;
红黑树的插入/删除–要能手绘插入过程(不太可能考代码,略复杂),删除操作也比较麻烦,也许不考;
三、红黑树的定义
红黑树是二叉排序树→左子树结点值≤ 根结点值≤ 右子树结点值;
红黑树与普通二叉排序树(BST)相比,有什么要求:
①每个结点或是红色,或是黑色的;
②根节点是黑色的;
③叶结点(外部结点、NULL
结点、失败结点)均是黑色的;
④不存在两个相邻的红结点(即红结点的父节点和孩子结点均是黑色);
⑤对每个结点,从该结点(不含该结点)到任一空叶结点(包含空叶结点)的简单路径上,所含黑结点的数目相同;
红黑树规则记忆口诀:左根右,根叶黑,不红红,黑路同;

红黑树的结点定义:
struct RBnode {
int key; //关键字的值
RBnode* parent; //父节点指针
RBnode* lChild; //左孩子指针
RBnode* rChild; //右孩子指针
int color; //结点颜色,如:可用0/1表示黑/红,也可使用枚举型enum表示颜色
};
【实例】一颗红黑树:

【练习】是否符合红黑树要求?
【一种可能的出题思路】
下列选项中,满足“红黑树”定义的是()
四、补充概念:结点的“黑高”
结点的黑高
b
h
bh
bh ——从某结点出发(不含该结点)到达任一空叶结点(包含空叶结点)的路径上黑结点总数;
五、红黑树的性质
性质1【定义④⑤可推】:从根节点到叶结点的最长路径不大于最短路径的
2
2
2倍;
性质2:有
n
n
n个内部节点的红黑树高度
h
⩽
2
l
o
g
2
(
n
+
1
)
h\leqslant2log_2{(n+1)}
h⩽2log2(n+1);
红黑树查找操作时间复杂度=
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n),查找效率与AVL树同等数量级;
六、红黑树的查找

红黑树的查找与BST、AVL 相同,从根出发,左小右大,若查找到一个空叶节点,则查找失败;
七、红黑树的插入
- 先查找,确定插入位置(原理同二叉排序树),插入新结点;
- 新结点是根——染为黑色;
- 新结点非根——染为红色;
- 若插入新结点后依然满足红黑树定义,则插入结束;
- 若插入新结点后不满足红黑树定义,需要进行调整,使其重新满足红黑树定义;
- 如何调整?看新结点叔叔的颜色:
- 黑叔:旋转+染色【对新结点所在子树进行旋转,对原先的儿、父或爷进行颜色的更换,旋转规则和颜色更换规则取决于新结点作为孙子结点的子树形态属于LL型、RR型、LR型、RL型中的哪一种,如下】;
- LL型:右单旋,父换爷+染色【父、爷进行颜色的更换】;
- RR型:左单旋,父换爷+染色【父、爷进行颜色的更换】;
- LR型:左、右双旋,儿换爷+染色【儿、爷进行颜色的更换】;
- RL型:右、左双旋,儿换爷+染色【儿、爷进行颜色的更换】;
- 红叔:染色+变新【对叔、父、爷都进行颜色的更换,爷结点因此而变成新结点,然后再看看是否影响了红黑树的定义,看看还要不要操作什么】;
- 黑叔:旋转+染色【对新结点所在子树进行旋转,对原先的儿、父或爷进行颜色的更换,旋转规则和颜色更换规则取决于新结点作为孙子结点的子树形态属于LL型、RR型、LR型、RL型中的哪一种,如下】;
【示例】从一棵空的红黑树开始,依次插入一组数字
这组数字为:20,10,5,30,40,57,3,2,4,35,25,18,22,23,24,19,18;
①插入:20,10,5【黑叔+LL型:右单旋+父爷色变】
- 黑叔:旋转+染色
- LL型:右单旋,父换爷+染色
②插入:30【红叔:叔父爷色变+爷更新】
- 红叔:染色+变新
- 叔父爷染色,爷变为新结点
③插入:40【黑叔+RR型:左单旋+父爷色变】
- 黑叔:旋转+染色
- RR型:左单旋,父换爷+染色
④插入:57【红叔:叔父爷色变+爷更新】
- 红叔:染色+变新
- 叔父爷染色,爷变为新结点
⑤插入:3,2【黑叔+LL型:右单旋+父爷色变】
- 黑叔:旋转+染色
- LL型:右单旋,父换爷+染色
⑥插入:4【红叔:叔父爷色变+爷更新】
- 红叔:染色+变新
- 叔父爷染色,爷变为新结点
⑦插入:35,25,18,22【红叔:叔父爷色变+爷更新】
- 红叔:染色+变新
- 叔父爷染色,爷变为新结点
⑧插入:23【黑叔+LR型:左右双旋+儿爷色变】
- 黑叔:旋转+染色
- LR型:左、右双旋,儿换爷+染色
⑨插入:24【红叔:叔父爷色变+爷更新,黑叔+LR型:左右双旋+儿爷色变】
- 红叔:染色+变新
- 叔父爷染色,爷变为新结点
- 黑叔:旋转+染色
- LR型:左、右双旋,儿换爷+染色
⑩插入:19,18【黑叔+RL型:右左双旋+儿爷色变】
- 黑叔:旋转+染色
- RL型:右、左双旋,儿换爷+染色

八、总结
九、其他问题
1、红黑树插入演示
参考:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
自我训练方法:你可以自己设计一些插入序列,每一次插入后,都先思考应该如何调整,然后在网站中演示验证。插入新元素时,尽可能覆盖黑叔LL/RR/LR/RL、红叔的情况。
从一棵空的红黑树开始,依次插入一组数字:20,10,5,30,40,57,3,2,4,35,25,18,22,23,24,19,18;
2、与“黑高”相关的推论
结点的黑高
b
h
bh
bh ——从某结点出发(不含该结点)到达任一叶结点的路径上黑结点总数;
思考1:根节点黑高为
h
h
h的红黑树,内部结点数(关键字)至少有多少个?
回答1:内部结点数最少的情况——总共
h
h
h层黑结点的满树形态;
结论:若根节点黑高为h,内部结点数(关键字)最少有
2
h
−
1
2^h-1
2h−1个;
红黑树的性质:
性质1:从根节点到叶结点的最长路径不大于最短路径的
2
2
2倍;
性质2:有
n
n
n个内部节点的红黑树高度
h
⩽
2
l
o
g
2
(
n
+
1
)
h\leqslant2log_2{(n+1)}
h⩽2log2(n+1);
红黑树查找操作时间复杂度=
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n),查找效率与AVL树同等数量级;
【证明】
性质1证明:任何一条查找失败路径上黑结点数量都相同,而路径上不能连续出现两个红结点,即红结点只能穿插在各个黑结点中间;
性质2证明:若红黑树总高度=
h
h
h,则根节点黑高
⩾
h
/
2
\geqslant h/2
⩾h/2,因此内部结点数
n
⩾
2
h
/
2
−
1
n \geqslant 2^{h/2}-1
n⩾2h/2−1,由此推出
h
⩽
2
l
o
g
2
(
n
+
1
)
h\leqslant2log_2{(n+1)}
h⩽2log2(n+1);

思考2:根节点黑高为
h
h
h的红黑树,内部结点数(关键字)至多有多少个?
回答2:内部结点数最多的情况——
h
h
h层黑结点,每一层黑结点下面都铺满一层红结点。共
2
h
2h
2h层的满树形态;
结论:若根节点黑高为
h
h
h,内部结点数(关键字)最多有
2
2
h
−
1
2^{2h-1}
22h−1个;
3、红黑树的删除
重要考点:
①红黑树删除操作的时间复杂度
=
=
=
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n);
②在红黑树中删除结点的处理方式和“ 二叉排序树的删除 ”一样;
③按②删除结点后,可能破坏“红黑树特性”,此时需要调整结点颜色、位置,使其再次满足“红黑树特性”。
免责声明:
1.编写此文是为了更好地学习数据结构,如果损害了有关人的利益,请联系删除;
2.如果文中描述欠妥,请在评论中进行指正;
3.文字编写不易,若感觉有用,点赞收藏关注会让博主很开心哦;
4.此外,本文支持任何形式的转载,转载请注明出处,非常感谢!!!
本文源自:https://blog.csdn.net/testleaf/article/details/126007281
参考:
https://www.cs.usfca.edu/~galles/visualization/RedBlack.html