目录
[一. 二叉搜索树(BST)](#一. 二叉搜索树(BST))
[二. 平衡二叉树(AVL树)](#二. 平衡二叉树(AVL树))
[2.2. 什么是AVL树](#2.2. 什么是AVL树)
[2.6.1. LL情况(左左情况)](#2.6.1. LL情况(左左情况))
[2.6.2 RR情况(右右情况)](#2.6.2 RR情况(右右情况))
[2.6.3. LR情况(左右情况)](#2.6.3. LR情况(左右情况))
[2.6.4. RL情况(右左情况)](#2.6.4. RL情况(右左情况))
[三. 红黑树(RBT)](#三. 红黑树(RBT))
[3.4.1 情况⼀:插⼊的是根节点](#3.4.1 情况⼀:插⼊的是根节点)
[3.4.2 情况⼆:叔叔是红⾊](#3.4.2 情况⼆:叔叔是红⾊)
[3.4.3 情况三:叔叔是⿊⾊](#3.4.3 情况三:叔叔是⿊⾊)
[编辑 3.6.红黑树和map与set](#编辑 3.6.红黑树和map与set)
对于⼆叉搜索树、平衡⼆叉树以及红⿊树,只需了解背后的原理,不做代码实现的要求。 重要清楚各种操作的时间复杂度,为使⽤set与map铺垫。
一. 二叉搜索树(BST)
二分搜索树(英语:Binary Search Tree),也称为 二叉查找树 、二叉搜索树 、有序二叉树或排序二叉树。满足以下几个条件:
- 若它的左子树不为空,左子树上所有节点的值都小于它的根节点。
- 若它的右子树不为空,右子树上所有的节点的值都大于它的根节点。
- 也就是左⼦树结点值<根结点值<右⼦树结点值。
它的左、右子树也都是二分搜索树。
如下图所示:

相较于堆,⼆叉搜索树是⼤⼩关系更为严格的数据结构。但是并不需要必须是⼀棵完全⼆叉树,也就 是树的形态是任意的。如下图所⽰,都是⼆叉搜索树:

根据⼆叉树的定义,左⼦树结点值<根结点值<右⼦树结点值,所以对⼆叉搜索树进⾏中序遍历,可 以得到⼀个递增的有序序列。
构造⼀颗⼆叉搜索树的⽬的,其实并不是为了排序,⽽是为了提⾼查找和插⼊删除关键字的速度。
1.1.查找操作
⼆叉搜索树的查找是从根结点开始,沿某个分⽀逐层向下⽐较的过程,
- 若⼆叉搜索树⾮空,先将给定值与根结点的关键字⽐较,
- 若相等,则查找成功;
- 若不等,如果⼩于根结点的关键字,则在根结点的 左⼦树上查找,否则在根结点的右⼦树上查找。
⽽这也是⼀个递归的过程。
以下实例在二分搜索树中寻找 43 元素

(1) 元素 43 比根节点 42 大,需要在右子节点继续比较。

(2) 元素 43 比 59 小,需要在左子节点继续比较。

(3) 元素 43 比 51 小,需要在左子节点继续比较。

(4) 查找 51 的左子节点 43,正好和相等,结束。
- 时间复杂度:
最坏情况下会从根节点开始,查找到叶⼦结点。因此时间复杂度是和树的⾼度有关的,⽽树⾼最差会 变成⼀条单链表,因此时间复杂度为O(N) 。
1.2.插入操作
⼆叉搜索树作为⼀种动态树表,其特点是树的结构通常不是⼀次⽣成的,⽽是在查找的过程的过程 中,当树中不存在关键字值等于给定值的结点时再进⾏插⼊的。插⼊的结点⼀定是⼀个新添加的叶结 点,且是查找失败时的查找路径上访问的最后⼀个结点的左孩⼦或右孩⼦。
- 若原⼆叉树为空树,则直接插⼊结点;
- 否则根据⼆叉搜索树的特性,将插⼊的关键字 key与根结点 对⽐,
- 若关键字 key ⼩于根结点值,则插⼊到左⼦树,
- 若关键字 key ⼤于根结点值,则插⼊到右 ⼦树。
以下实例向如下二分搜索树中插入元素 61 的步骤:

(1)需要插入的元素 61 比 42 大,比较 42 的右子树根节点。

(2)61 比 59 大,所以需要把 61 移动到 59 右子树相应位置,而此时为空,直接插入作为 59 的右子节点。

插入操作也是一个递归过程,分三种情况,等于、大于、小于。
- 时间复杂度
插入与查找过程一致,因此时间复杂度为O(N).
1.3.构造二叉搜索树(BST树)
⼆叉搜索树的构造就是不断向原来的树中插⼊新的结点即可。
案例一:根据序列a={51,68,59,27,25,33,75,70} 比特就业课 ,构造⼀棵⼆叉排序树

案例二:根据序列a={25,27,33,59,75,51,70,68} ,构造⼀棵⼆叉排序树

由此可⻅,虽然结点的值都相同,不同的构造顺序会有产⽣不同的⼆叉搜索树,会影响查找和插⼊的 效率。并且,构造序列越有序,⼆叉搜索树的查找效率越低。
1.4.删除操作
我们默认需要按值 来删除元素,所以要先进行查找,找到之后再删除该节点。删除节点之后,为了保持二叉搜索树的原有特性,就需要调整其他节点。
根据被删除的节点,有四种情况需要分析讨论:
1. 被删除节点的左右孩子均为空

2. 被删除的节点只有左孩子

3. 被删除的节点只有右孩子

4. 被删除的节点既有左孩子,又有右孩子
若结点既有左子树,又有右子树,有两种策略:
- a. 令结点的直接后继替代该结点,然后从二叉搜索树中删去这个直接后继。
- 结点的直接后继:中序遍历中,该结点的后继,也就是该结点的右子树最左下结点(该结点一定没有左子树)。
- b. 令结点的直接前驱替代该结点,然后从二叉搜索树中删去这个直接前驱。
- 结点的直接前驱:中序遍历中,该结点的前驱,也就是该结点的左子树最右下结点(该结点一定没有右子树)。

这里说明一下寻找右子树最小值 的方法:从右孩子开始,一直访问其左孩子节点,直到访问到空为止。这里访问到的最后一个节点即是右子树的最小值。(左子树最大值也同理)
案例⼀:删除50这个结点,并⽤直接前驱来替代。

案例二:删除50这个结点,并⽤直接后驱来替代。

- 时间复杂度:
查找前驱和后继的操作,最差也会遍历整个⼆叉树,因此时间复杂度为 O(N) 。
二. 平衡二叉树(AVL树)
2.1.为什么要有平衡二叉树
二叉搜索树一定程度上可以提高搜索效率,但是当原序列有序时,例如序列 A = {1,2,3,4,5,6},构造二叉搜索树如图。依据此序列构造的二叉搜索树为右斜树,同时二叉树退化成单链表,搜索效率降低为 O(n)。

在此二叉搜索树中查找元素 5需要查找 5 次。
二叉搜索树的查找效率取决于树的高度,因此保持树的高度最小,即可保证树的查找效率。同样的序列 A,将其改为下图的方式存储,查找元素 5 时只需比较 3 次,查找效率提升一倍。

可以看出当节点数目一定,保持树的左右两端保持平衡,树的查找效率最高。
这种左右子树的高度相差不超过 1 的树为平衡二叉树。
2.2. 什么是AVL树
为了保证二叉搜索树的性能,规定在插入和删除节点时,要保证任意节点的左、右子树高度差的绝对值不超过1,这样的二叉树称为平衡二叉树(简称AVL树)。
其中节点左子树与右子树的高度差 定义为该节点的平衡因子 (一般是左子树的高度减去右子树的高度,当然反过来也是可以的,下面我举的例子全都是左子树的高度减去右子树的高度)。由此可知,平衡二叉树中,每一个节点的平衡因子只可能是-1、0或1。
如下图所示:节点上方的数字表示平衡因子。
左图是一棵平衡二叉树,右图不是一棵平衡二叉树

2.3.平衡因子
AVL 树的每个节点都有一个平衡因子(Balance Factor),其定义为:
- 平衡因子 = 左子树高度 - 右子树高度
AVL 树要求每个节点的平衡因子的绝对值不超过 1,也就是平衡因子只能取 -1、0 或 1。
示例 1:平衡因子都为 0 的 AVL 树
cpp
5(0)
/ \
3(0) 7(0)
/ \ / \
2(0) 4(0) 6(0) 8(0)
各节点的平衡因子计算:
- 节点 2:左子树高度为 0,右子树高度为 0,平衡因子 = 0 - 0 = 0。
- 节点 4:左子树高度为 0,右子树高度为 0,平衡因子 = 0 - 0 = 0。
- 节点 3:左子树(节点 2)高度为 1,右子树(节点 4)高度为 1,平衡因子 = 1 - 1 = 0。
- 节点 6:左子树高度为 0,右子树高度为 0,平衡因子 = 0 - 0 = 0。
- 节点 8:左子树高度为 0,右子树高度为 0,平衡因子 = 0 - 0 = 0。
- 节点 7:左子树(节点 6)高度为 1,右子树(节点 8)高度为 1,平衡因子 = 1 - 1 = 0。
- 节点 5:左子树(以节点 3 为根)高度为 2,右子树(以节点 7 为根)高度为 2,平衡因子 = 2 - 2 = 0。
示例 2:包含平衡因子为 -1 和 1 的 AVL 树
cpp
8(0)
/ \
4(1) 12(-1)
/ \
2(0) 14(0)
各节点的平衡因子计算:
-
- 节点 2:左子树高度为 0,右子树高度为 0,平衡因子 = 0 - 0 = 0。
-
- 节点 4:左子树(节点 2)高度为 1,右子树高度为 0,平衡因子 = 1 - 0 = 1。
-
- 节点 14:左子树高度为 0,右子树高度为 0,平衡因子 = 0 - 0 = 0。
-
- 节点 12:左子树高度为 0,右子树(节点 14)高度为 1,平衡因子 = 0 - 1 = -1。
-
- 节点 8:左子树(以节点 4 为根)高度为 2,右子树(以节点 12 为根)高度为 2,平衡因子 = 2 - 2 = 0。
示例 3:平衡因子为 -2 的 BST 树,不是 AVL 树
cpp
10(-2)
/ \
5(0) 20(0)
/ \
15(0) 30(0)
各节点的平衡因子计算:
-
- 节点 5:左子树高度为 0,右子树高度为 0,平衡因子 = 0 - 0 = 0。
-
- 节点 15:左子树高度为 0,右子树高度为 0,平衡因子 = 0 - 0 = 0。
-
- 节点 30:左子树高度为 0,右子树高度为 0,平衡因子 = 0 - 0 = 0。
-
- 节点 20:左子树(节点 15)高度为 1,右子树(节点 30)高度为 1,平衡因子 = 1 - 1 = 0。
-
- 节点 10:左子树(节点 5)高度为 1,右子树(以节点 15 为根的子树和节点 20 及其子树共同构成右子树部分)高度为 3,平衡因子 = 1 - 3 = -2。
2.4.最小不平衡子树
在⼆叉搜索树中插⼊新结点之后,插⼊路径的点中,可能存在很多平衡因⼦的绝对值⼤于 的,此时 找到距离插⼊结点最近的不平衡的点 ,以这个点为根的⼦树就是最⼩不平衡⼦树。如下图:

右图的70就是距离插入的节点67的最近的 不平衡结点。
2.5.查找操作
与⼆叉搜索树的查找⼀样:
从根结点开始,沿某个分⽀逐层向下⽐较的过程,
若⾮空,先将给定值与 根结点的关键字⽐较,若相等,则查找成功;
若不等,如果⼩于根结点的关键字,则在根结点的左⼦ 树上查找,否则在根结点的右⼦树上查找。
- 时间复杂度:
平衡⼆叉树会限制树的⾼度不会过⾼,趋近于 log n 级别,因此时间复杂度为 O(logN) 。
2.6.插入操作
要将一棵普通的二叉搜索树(BST)转换为平衡的 AVL 树,可以通过以下几个关键步骤来实现,主要思路是对最小不平衡子树进行旋转操作,使得树重新达到平衡状态。
关于调整子树平衡的感性理解
至于为什么调整这一棵子树就可以让所有节点平衡?我们可以感性理解一下:
- 本来整棵树就是平衡二叉树,如果来了一个节点导致失衡,那么失衡节点的平衡因子只能是 2 或 -2。
- 当我们把最小不平衡子树调整平衡之后,那么这棵子树的高度就会减 1,向上传递的过程中,会让整个路径里面的平衡因子都向 0 靠近一位,原来的 1 会变成 0,原来的 -1 会变成 0,整棵树就变得平衡了。
具体有四种旋转情况:
然后将其平衡的结果也很容易(不考虑其附带节点只看结果),将中间大小数值移动最上方,其他相对位置不变即可。
2.6.1. LL情况(左左情况)
在某个节点A的左子树的左子树上插入新节点,导致该节点A的平衡因子的绝对值大于 1。

此时需要将L右旋:
- 将结点L向右上旋转代替结点T作为根结点;
- 将节点T向右下旋转作为结点L的右⼦树的根结点;
- 结点L的原右⼦树(LR)则作为结点T的左⼦树。

案例:下列AVL树中插⼊1 。

最⼩不平衡⼦树是以13为根的⼦树,引起不平衡的原因是13的左孩⼦的左⼦树上插⼊⼀个新的结 点,因此需要右旋⼀次。右旋的结点为 10:

2.6.2 RR情况(右右情况)
RR表⽰:新结点由于插⼊在T结点的右孩⼦(R)的右⼦树(RR)中,从⽽导致T结点失衡。如下图所⽰:

此时需要⼀次向左的旋转操作,将R左旋:
- 将结点R向左上旋转代替结点T作为根结点;
- 将节点T向左下旋转作为结点R的左⼦树的根结点;
- 结点R的原左⼦树(RL)则作为结点T的右⼦树。
如下图:

案例:下列AVL树中插⼊ 64。

最⼩不平衡⼦树是以49为根的⼦树,引起不平衡的原因是49的右孩⼦的右⼦树上插⼊⼀个新的结 点,因此需要左旋⼀次。左旋的结点为59:

2.6.3. LR情况(左右情况)
LR表⽰:新结点由于插⼊在T结点的左孩⼦(L)的右⼦树(LR)中,从⽽导致T结点失衡。

此时需要两次旋转操作,先将LR左旋,再将LR右旋。
将LR左旋:
- 将结点LR向左上旋转代替结点L作为根结点;
- 将节点L向左下旋转作为结点LR的左⼦树的根结点;
- 结点LR的原左⼦树(LRL)则作为结点L的右⼦树。
将LR右旋:
- 将结点LR向右上旋转代替结点T作为根结点;
- 将节点T向右下旋转作为结点LR的右⼦树的根结点;
- 结点LR的原右⼦树(LRR)则作为结点T的左⼦树。
如下图:

案例:下列AVL树中插⼊ 1。

最⼩不平衡⼦树是以49为根的⼦树,引起不平衡的原因是49的左孩⼦的右⼦树上插⼊⼀个新的结 点,因此需要左旋⼀次,然后右旋⼀次。旋转的结点为45:

2.6.4. RL情况(右左情况)
RL表⽰:新结点由于插⼊在T结点的右孩⼦(R)的左⼦树(RL)中,从⽽导致T结点失衡。如下图所⽰:

此时需要两次旋转操作,先将RL右旋,再将RL左旋。
将RL右旋:
- 将结点RL向右上旋转代替结点R作为根结点;
- 将节点R向右下旋转作为结点RL的右⼦树的根结点;
- 结点RL的原右⼦树(RLR)则作为结点R的左⼦树。
将RL左旋:
- 将结点RL向左上旋转代替结点T作为根结点;
- 将节点T向左下旋转作为结点RL的左⼦树的根结点;
- 结点RL的原左⼦树(RLL)则作为结点T的右⼦树。
如下图:

案例:下列AVL树中插⼊52。

最⼩不平衡⼦树是以49为根的⼦树,引起不平衡的原因是49的右孩⼦的左⼦树上插⼊⼀个新的结 点,因此需要右旋⼀次,然后再左旋⼀次。旋转的结点为55:


- 插⼊操作的时间复杂度:
旋转操作仅需修改指针,因此最⼤的时间开销就是先把结点插⼊到空结点的位置,时间复杂度和查找 ⼀致,因此为O(logN)
2.7.构造AVL树
平衡⼆叉树的构造,就是不断向树中插⼊新的结点。
a ={15,6,10,17,11,13,9,20,16,22} ,构造⼀棵⼆叉排序树


2.8.删除操作
-
执行二叉搜索树的删除操作:
-
如果删除节点是叶子节点,直接删除。
-
如果删除节点只有一个子节点,用该子节点替换删除节点。
-
如果删除节点有两个子节点,通常用其右子树中最小节点(该节点无左子节点)或左子树中最大节点(无右子节点)代替被删除节点,再删除替代节点。
-
-
从删除节点向上找到第一个不平衡的节点T:
- 从删除节点开始,向上遍历,找到第一个平衡因子绝对值大于1的节点T,即最小不平衡子树的根节点。
-
确定X和Y节点:
-
X为节点T的最高子树的根节点,即T的左孩子或右孩子中高度较高的那个。
-
Y是节点X的最高子树的根节点,即X的左孩子或右孩子中高度较高的那个。
-
-
对以Y为根的子树进行平衡调整:
-
根据Y的位置情况,有以下四种调整方式:
-
LL(左左)情况:X是T的左孩子,Y是X的左孩子。对T进行右单旋。
-
LR(左右)情况:X是T的左孩子,Y是X的右孩子。先对X进行左旋,再对T进行右旋。
-
RR(右右)情况:X是T的右孩子,Y是X的右孩子。对T进行左单旋。
-
RL(右左)情况:X是T的右孩子,Y是X的左孩子。先对X进行右旋,再对T进行左旋。
-
-
-
继续向上寻找并调整:
- 调整后,继续向上寻找下一个不平衡的子树,重复步骤4,直到根节点为止。
案例⼀:删除30。

叶⼦结点直接删除,删除之后向上到根节点,所有结点都没有失衡,直接结束。
案例⼆:删除10.
第⼀次调整:从10向上找,第⼀个不平衡⼦树是以49为根的⼦树,⾼度最⾼的⼉⼦是59,⾼度 最⾼的孙⼦是69,呈现右孩⼦右孩⼦,因此仅需对59左旋⼀次。

没有第⼆次调整了,因为所有都已经平衡了。
案例三:删除57。

第⼀次调整:从57向上找,第⼀次出现的不平衡⼦树是以49为根的⼦树,⾼度最⾼的⼉⼦是 45,⾼度最⾼的孙⼦是47,呈现左孩⼦右孩⼦,因此需要让47左旋⼀次,然后再右旋⼀次:
第⼆次调整:从47向上找,发现59失衡,找到最⾼的孩⼦为69号结点,最⾼的孙⼦为76号结 点,呈现的关系是右孩⼦右孩⼦,因此将69左旋⼀次:
调整结束,因为已经到了根结点,并且根节点是平衡的。
- 时间复杂度:
由于可能会调整很多次,最差情况下会从叶⼦结点开始向上到根,整个过程遍历⼀个树⾼,因此时间 复杂度为O(logN)
三. 红黑树(RBT)
3.1.什么是红黑树
红⿊树(简称RBT),也是⼀棵⼆叉搜索树。
它是在搜索树的基础上,使每个结点上增加⼀个存储位表⽰ 结点的颜⾊,可以是Red或者Black,通过对任意⼀条从根到叶⼦的路径上各个结点着⾊⽅式的限 制,确保没有⼀条路径会⽐其他路径⻓出2倍,因⽽是**⼀棵接近平衡的⼆叉搜索树**。
红⿊树相对于AVL树来说,牺牲了部分平衡性以换取插⼊/删除操作时少量的旋转操作,整体来说性能 要优于AVL树。
在⼀棵红⿊树中,需要满⾜下⾯⼏条规则,在每次插⼊和删除之后,都应该让红⿊树满⾜下⾯的规 则:
- 每个结点要么是红⾊要么是⿊⾊;
- 根节点和叶⼦结点 (这⾥的叶⼦结点不是常规意义上的叶⼦结点,⽽是空结点,如下图中的NIL) 是⿊⾊的;
- 3. 如果⼀个结点是红⾊的,则它的两个孩⼦结点必须是⿊⾊的,也就是说任意⼀条路径不会有连续的 红⾊结点;
- 4. 对于任意⼀个结点,从该结点到其所有叶⼦结点的路径上,均包含相同数量的⿊⾊结点。
例如下⾯的红⿊树:

简单练习:判断下⾯的树,是否是红⿊树,为了简介,叶⼦结点就不画了



-
图一不是红黑树,因为出现两个连续的红色。
-
图二不是红黑树,因为根节点应该是黑色的。
-
图三不是红黑树,因为从根节点到叶子节点的所有路径中的黑色节点数目不同。
-
图四不是红黑树,红黑树的首要前提应该是二叉搜索树。
-
图五图六均是红黑树。
3.2.红黑树的性质
根据红黑树的规则,我们可以得出红黑树的两个重要性质:
1. 从根结点到叶结点的最⻓路径不⼤于最短路径的2倍。
-
比较容易证明,这里就不用严谨的数学方式了,直接看一个具体的例子感受感受。
-
如下图所示,最短路径最短有 3 个结点,全是黑色。因为不能出现连续的红色,所以想要最长,必须得是红⿊相间的形式,最长就是 5,不会超过最短路径的 2 倍。

2. 有 n 个结点的红⿊树,高度 h ≤ 2log2(n+1),这意味着查找时间复杂度为 O(logN)。
3.3.红黑树的查找
-
查找过程:与二叉搜索树的查找过程相同。从根节点开始,将目标值与当前节点的关键字进行比较:
-
如果目标值等于当前节点的关键字,则查找成功。
-
如果目标值小于当前节点的关键字,则在左子树上继续查找。
-
如果目标值大于当前节点的关键字,则在右子树上继续查找。
-
如果一直搜索到空节点(NIL),则查找失败。
-
-
时间复杂度:由于红黑树是一种自平衡的二叉搜索树,其高度被限制在 O(logn) 的范围内,因此查找操作的时间复杂度为 O(logn)。
3.4.红黑树的插入操作
第⼀步,也是先按照⼆叉搜索树的插⼊⽅式插⼊新的结点。
接下来思考⼀个⼩问题:新插⼊的结点染 成红⾊还是⿊的好呢?
答:明显是红⾊较好。
如果染成⿊⾊,⼀定会让这条路径上的⿊⾊结点数量增多,那么就需要调整所有从根节点到叶⼦结点 的路径,使其重新符合红⿊树的特性。不仅每次插⼊都要调整,⽽且调整的规模还⾮常庞⼤。
但是,如果染成红⾊,有可能就不需要调整。如果需要调整,也只会破坏根节点不能为红,以及不能出现连续的红⾊结点这两个规则。
因此红⿊树的插⼊过程⼤致为:
- 1. 按照⼆叉搜索树的插⼊⽅式插⼊新的结点;
- 2. 默认该点是红⾊,如果破坏了红⿊树的规则,然后就分情况讨论。
接下来,就详细讨论插⼊新结点之后会遇到的所有情况,以及每种情况需要如何调整。 为了后续叙述⽅便,标记新插⼊的结点为c(cur),⽗结点为p(parent),⽗亲的⽗结点为 g(grandfather),⽗结点的兄弟为u(uncle)。

3.4.1 情况⼀:插⼊的是根节点
这是第⼀次插⼊结点,直接将结点的颜⾊变成⿊⾊即可。

3.4.2 情况⼆:叔叔是红⾊

这种情况下,不需要旋转,只需要不断变⾊即可。
具体的策略是:
• ⽗亲、叔叔和爷爷同时变⾊,然后将爷爷看做新插⼊的结点,继续向上判断。
- p和u变成⿊⾊,g变成红⾊。这样就相当于g所在的⼦树增加⼀个⿊⾊结点,⼜减少⼀个⿊⾊结 点,整个路径⿊⾊结点的数量就不会改变。

- 但是,由于g变成红⾊,有可能g的⽗亲也是红⾊,或者g是⼀个根,此时就需要把g当成新插⼊ 的结点,继续判断。

3.4.3 情况三:叔叔是⿊⾊

这种情况需要继续分类讨论,根据祖⽗、⽗亲、新结点三者的位置,分情况旋转+变⾊。这⼀块的旋 转操作和平衡⼆叉树的旋转⼀样,⽆⾮就是多了⼀个变⾊,因此不需要有太⼤的⼼理负担~
LL 型-右单旋+⽗爷变⾊
如果⽗亲和新结点的位置关系相对于爷爷呈现:新结点是爷爷的左孩⼦的左孩⼦,仅需两步:
- 右旋⽗亲结点;
- 然后将⽗亲和爷爷变⾊

RR 型-左单旋+⽗爷变⾊
如果⽗亲和新结点的位置关系相对于爷爷呈现:新结点是爷爷的右孩⼦的右孩⼦,仅需两步:
- 左旋⽗亲结点;
- 然后将⽗亲和爷爷变⾊

LR 型-左右双旋+⼉爷变⾊
如果⽗亲和新结点的位置关系相对于爷爷呈现:新结点是爷爷的左孩⼦的右孩⼦,仅需两步:
- 新结点先左旋,再右旋;
- 然后将新结点和爷爷结点变⾊

RL 型-右左双旋+⼉爷变⾊
如果⽗亲和新结点的位置关系相对于爷爷呈现:新结点是爷爷的右孩⼦的左孩⼦,仅需两步:
- 新结点先右旋,再左旋;
- 然后将新结点和爷爷结点变⾊

3.5.红⿊树的构造
红⿊树的构造,就是不断向红⿊树中插⼊新的结点。
案例:根据序列 a ={18,19,25,40,30,11,9,1,6,4} ,构造⼀棵红⿊树。

3.6.红黑树和map与set
- 红黑树是底层实现机制 :在很多编程语言的标准库中,
map
和set
的底层实现是基于红黑树的。例如,在 C++ 的 STL(标准模板库)中,std::map
和std::set
是基于红黑树实现的。红黑树是一种自平衡的二叉查找树,具有良好的插入、删除和查找性能,能够保证这些操作的时间复杂度为 O(log n)。