目录
2.3.情况二:单旋+变色(u为null(不存在)或颜色为黑色时)
[子情况 2‑1:p 是 g 的左孩子,c 是 p 的左孩子(左‑左构型,右单旋)](#子情况 2‑1:p 是 g 的左孩子,c 是 p 的左孩子(左‑左构型,右单旋))
[子情况 2‑2:p 是 g 的右孩子,c 是 p 的右孩子(右‑右构型,左单旋)](#子情况 2‑2:p 是 g 的右孩子,c 是 p 的右孩子(右‑右构型,左单旋))
2.4.情况三:双旋+变色(u为null(不存在)或颜色为黑色时)
[子情况 3‑1:p 是 g 的左孩子,c 是 p 的右孩子(LR 左‑右构型,先左单旋,再右单旋)](#子情况 3‑1:p 是 g 的左孩子,c 是 p 的右孩子(LR 左‑右构型,先左单旋,再右单旋))
[子情况 3‑2:p 是 g 的右孩子,c 是 p 的左孩子(RL 右‑左构型,先右单旋,再左单旋)](#子情况 3‑2:p 是 g 的右孩子,c 是 p 的左孩子(RL 右‑左构型,先右单旋,再左单旋))
引言:
红黑树是一种近似平衡的二叉搜索树 ,在普通 BST 的基础上,通过节点颜色约束 实现弱平衡 。它摒弃了 AVL 树严格的高度差限制,以宽松的颜色规则约束树高,保证任意根到叶子路径的长度不超过最短路径的 2 倍。
相比于普通 BST 容易退化成链表、AVL 树旋转过于频繁的缺陷,红黑树在查询、插入、删除 的综合性能更均衡。虽然时间复杂度同样为 O(logN),但红黑树平衡约束更宽松,写操作触发的旋转次数极少,是工程中综合效率最高的平衡二叉树,也是 C++ STL 中 map/set 的底层核心数据结构。
本文将从红黑树规则原理、平衡推导、与 AVL 树对比 入手,循序渐进讲解红黑树核心思想,并从零手搓实现红黑树插入、旋转、校验完整逻辑,帮助彻底吃透红黑树核心精髓。
提示:阅读本文前,需要掌握AVL树------------C++ 高阶数据结构:AVL 树万字详解|原理、四种旋转、key‑value 实现、调试校验、set/map/红黑树 底层前置知识-CSDN博客
那么话不多说,接下来进入正文------------------------------>

红黑树的概念
红黑树是一棵二叉搜索树,每个结点会额外保存颜色标记,颜色分为红色、黑色两种。
依靠五条颜色约束规则 ,红黑树保证:从根到哨兵叶子的所有路径中,最长路径长度不会超过最短路径的 2 倍,实现近似平衡,避免普通 BST 退化成链表,也避免像AVL树那样频繁的旋转
1.红黑树的规则
- 每个结点,颜色只能是红色或者黑色。
- 根结点是黑色。
- 所有 NIL结点(树中的NULL叶子结点) 为黑色。
- 若一个结点是红色,则它的左右孩子都必须是黑色,不存在连续红色结点。
- 对任意结点,从该结点出发,到它所有后代 NIL 叶子的每一条简单路径(要走到NIL叶子(NULL节点))上,黑色结点数目相等(黑高相等)
注: 这里的NIL 哨兵(外部结点) ,不是存储业务数据的普通叶子。理论上依靠 NIL 统一路径边界;但实际编码不会实例化 NIL 对象,直接用空指针
NULL来充当 NIL,这个看图更好理解这个NIL其实在日常时候不需要怎么管,把NIL哨兵理解成NULL节点就可以了,每条路径以NULL为结束(NIL只要有个概念就行)
这里我放几个红黑树的图
这个图有9条路径(到NIL),有一条最短路径,有四条最长路径

这个图有10条路径,有四条最长路径,没有最短路径

这个图有16条路径,全是最长路径,没有最短路径

这个图有8条路径,全是最短路径,没有最长路径

2.思考:红黑树如何确保最长路径不超过最短路径的2倍
首先,我们通过第五点(黑高相等)和第三点NIL全为黑色,我们在推导的时候可以不用考虑NIL节点对路径的影响,只需要考虑我们看得见的节点就可以了
由规则 5(黑高相等)可知,从根到达 NIL 哨兵的每一条路径,黑色结点数量完全相同,这个数值记作bh,也就是树的黑高。这里的bh我们不包含NIL节点
极端场景下最短的数据结点路径,整条路径全部由黑色结点构成。
根据规则 4,树中不允许出现连续红色结点。因此路径上红色结点最多只能与黑色结点交替出现,极端最长路径就是黑、红交替排布。由于黑色结点总数固定为bh(不包含NIL节点)交替排布(黑红黑红)下得到的最长路径就是2*bh(不包含NIL节点)
附:包含NIL节点的情况下,最短路径是bh+1,最长路径就是2*bh+1
注 :全黑的最短路径、一黑一红交替的最长路径,这两种极端情况并不会在每一棵红黑树中同时出现 。设任意一条从根出发到 NIL 的路径中,数据结点的总个数为 h(不包含NIL节点),则满足不等式:bh<= h <=2*bh
3.红黑树的效率
假设 N 是红黑树的数据结点总数,h 代表最短路径的数据结点长度(全黑路径,等价前文自定义 bh)。 根据二叉树结点数量与高度关系,可以得到结点数量范围:
由此可得
;最坏情况需要遍历最长路径,路径长度约为
。 虽然最坏路径存在 2 倍常数系数,但时间复杂度分析会忽略常数项,因此红黑树增、删、查、改的时间复杂度依旧为O(logN)。

红黑树相比 AVL 树更加抽象:AVL 树依靠结点高度差直接控制强平衡;红黑树依靠五条颜色约束规则间接实现近似平衡。
二者时间复杂度属于同一量级,但红黑树对平衡约束更加宽松: AVL 删除时最坏会产生
次旋转;红黑树插入最多 2 次旋转、删除最多仅 3 次旋转。整体来看,在相同结点规模下,红黑树旋转总次数更少,写操作开销更低。
因此 C++ STL 中map/set选择红黑树,而非 AVL 树。
4.红黑树与AVL树对比
平衡条件
- AVL 树属于强平衡二叉搜索树,依靠平衡因子约束,保证左右子树高度差不超过 1;
- 红黑树是弱平衡二叉搜索树,不严格约束高度差,依靠五条颜色规则,限制最长路径不超过最短路径 2 倍。
性能差异
- 查询:AVL 树树高更低,查询理论略快;但两者时间复杂度都是\(O(logN)\)。
- 插入:AVL 插入最多 1 次旋转;红黑树插入最多 2 次旋转,很多场景仅变色无需旋转。
- 删除:这是两者最核心区别,AVL 删除最坏会触发\(O(logN)\)次旋转;红黑树删除最多 3 次旋转,写操作代价更小。
空间与工程使用
两者结点额外开销很小:AVL 需要保存平衡因子,红黑树只需要保存颜色标记。
AVL 更适合读多写极少的场景;红黑树读写综合表现更好,C++ STL 的 map、set 底层选用红黑树。
红黑树的实现
我们这里依旧来实现key_value,无冗余版本
1.红黑树的结构
红黑树一个节点所包含的成员相比AVL树而言,就是少了平衡因子,但是多了标记颜色的枚举成员
重要:新创建出来的节点默认设置为红色。如果初始为黑色,会直接破坏路径黑高,修复代价巨大。具体会在插入部分讲
我这里先实现裸nullptr版本(一般我们自己实现的都是这个版本),不是 STL 的 NIL 哨兵模式,后续写修复逻辑的时候,获取叔叔、祖父需要额外做空指针判断。
代码如下
cpp
#pragma once
#include <iostream>
#include<utility>//pair头文件
enum Color
{
Red,
Black
};
template<class K,class V>
struct RBTreeNode
{
using Node = RBTreeNode<K, V>;
RBTreeNode() = default;
RBTreeNode(const std::pair<K, V>& kv)
:_kv(kv)
, _col(Red)
{
}
std::pair<K, V> _kv;
Node* _left = nullptr;
Node* _right = nullptr;
Node* _parent = nullptr;
Color _col;
};
template<class K,class V>
class RBTree
{
using Node = RBTreeNode<K, V>;
public:
private:
Node* _root = nullptr;
};
2.红黑树的插入
2.1.红黑树插入一个值的大概过程
- 插入一个值,先按照二叉搜索树的规则完成插入,插入完成后再校验是否满足红黑树规则。
- 如果是空树插入:新增节点作为根节点,根节点需要设置为黑色。 如果是非空树插入:新增节点默认是红色。倘若新增节点为黑色,会直接破坏「每条路径黑色节点数量相等」这规则 ,黑高的失衡很难修复,因此新节点必须为红色。
- 非空树插入完成,新节点为红色:
如果父节点p是黑色:没有违反任何红黑树规则,插入直接结束,无需修复。 - 如果父节点
p是红色:此时出现连续的红色节点,违反规则 。 由于p是红色,那么祖父节点g一定是黑色。 此时c(cur 当前节点) 红、p(parent 父节点) 红、g(grandfather 祖父节点) 黑,三者颜色固定 。后续处理的关键取决于叔叔节点u(uncle,父节点的兄弟) 的颜色,根据u的不同情况分支处理。
附:
c(cur):当前需要处理的节点(插入的点)p(parent):c 的父节点g(grandfather):c 的祖父节点u(uncle):g 的孩子,p 的兄弟,c 的叔叔节点
2.2.情况一:变色(u存在且颜色为红色时)
处理操作:
- 将父节点
p、叔叔节点u修改为黑色;祖父节点g修改为红色。 - 最后将
g作为新的cur节点,继续向上迭代修复。

原理深度分析
插入新节点为红色,此时出现 c、p 连续红色节点,违反红黑树规则。
这里有一个非常关键的逻辑:我们绝对不能修改当前节点 c 的颜色。 前面我们强调过:树中非根节点新增节点默认必须插红色。如果将新节点 c 改为黑色,会直接让当前路径黑高加一,导致整棵子树黑高彻底失衡,这种失衡代价极大、极难维护。
所以我们只能将父节点 p 改为黑色,用来解决「连续双红」的问题。
但 p 变黑后又会出现新问题: c 所在路径凭空多了一个黑色节点,左右子树黑高再次不平衡。 为了抵消这一层黑色增量,我们把 祖父 g 改为红色。
此时 c 这一条路径黑高恢复正常,但是 u 所在路径黑高又失衡了 。 所以我们必须把 u 也改为黑色,补齐另一侧的黑色数量,让左右子树黑高再次统一。
这就是该情况必须满足「u 存在且为红色」的终极原因
- 如果 u 不存在(nullptr):无法补黑色,另一侧黑高永久失衡,无法修复;
- 如果 u 原本就是黑色:变色无效,左右黑高依旧不相等。 因此只有 u 存在且为红,这套变色逻辑才能成立。
变色完成后,当前子树的黑高完全合法、双红问题解决。 但是 g 从黑色被改成了红色 ,存在向上传递风险: 如果 g 的父节点也是红色,会再次出现双红冲突。 所以我们需要把 g 设置为新的 cur,继续向上循环修复。 直到 cur 父节点为黑、或者 cur 变为根节点(最后根强制变黑)为止。
核心特点
情况 1 只变色、不旋转。 无论 c 是 p 的左 / 右孩子,无论 p 是 g 的左 / 右孩子,处理逻辑完全一致。
和 AVL 树类似,处理操作的演示图只是展示其中一种具象样例,现实中触发该分支的构型有海量变种。
下面我们使用抽象模型,来理解通用场景:
抽象示意图

不同复杂度的场景
hb=0(子树全部为空)

hb=1的复杂子树场景

hb=2,海量组合的高阶子树场景

- 抽象示意图中:
d/e/f代表黑高为hb的合法红黑子树;a/b代表黑高为hb‑1、根节点为红色的合法红黑子树,hb >= 0。 hb=0、hb=1、hb=2分别对应不同复杂度的子树组合;当hb=2时,理论组合数量甚至达到百亿级别。
但无论子树内部多么复杂,情况 1 的处理逻辑永远不变:p、u 变黑,g 变红,将 g 作为新 cur 向上递归处理。 所以我们只需要记住抽象模型,不用穷举所有具体构型。
2.3.情况二:单旋+变色(u为null(不存在)或颜色为黑色时)
关键区分与黑高推导:
当u不存在时:c一定是本次新插入的红色节点。新插入的节点是红节点,不会影响原树的黑高,只会产生双红情况。 如果c是新插入节点,但u是存在的,那么原树的黑高就是不相等的,这种构型不可能出现。所以 c 是新插入的节点只对应 u 不存在的场景。
当u存在且为黑色时:c绝对不是本次新插入节点。c原先就存在,本来是黑色;下层子树插入节点触发更新逻辑,向上更新到 c 节点,把 c 节点变为了红色。只有这样才能够满足黑高的规则。
这里有一个很容易踩的误区:看图的时候可能会觉得,即便 c 原本是黑节点,p 红、g 黑、u 黑,p 的右边路径黑高会变少,造成树不平衡。但事实不是这样。 因为 p 的右边一定存在合法子树,我们每一次插入都会完成下层调整,保证下层已经是合法红黑树。 假设 c 节点向下所有路径的黑节点数量为h,那么 p 节点右边那条路径的黑节点就是h+1个;u 节点左右两条路径的黑节点全都是h个。 各条路径的黑节点计数,正是我们红黑树做颜色调整的重要依据。
处理操作:
- 以AVL树的逻辑进行单旋
- 根据具体情况更新红黑树节点颜色(保证黑高相等)------只记结论就是g变红,p变黑

原理深度分析
想要消除c与p连续红色冲突,必须让p变成黑色。
但此时叔叔u为空或者黑色,只靠单纯变色已经无法平衡两侧黑高,所以必须搭配旋转 + 变色共同处理。
子情况 2‑1:p 是 g 的左孩子,c 是 p 的左孩子(左‑左构型,右单旋)
以g作为旋转支点执行右单旋;
旋转完成后,将p置为黑色,g置为红色。 旋转之后p成为这棵子树新的根。 这样子树内部路径黑色节点总数保持不变,同时消除连续红色。 不需要继续向上迭代修复 :因为子树根现在是p(黑色),不管p的上层父节点是红、黑还是空,都不会产生新的连续红色冲突。
下图是还原流程

子情况 2‑2:p 是 g 的右孩子,c 是 p 的右孩子(右‑右构型,左单旋)
以g作为旋转支点执行左单旋;
旋转完成后,将p置为黑色,g置为红色。 旋转之后p成为这棵子树新的根。 子树黑色节点数量不变,消除连续红色冲突。 同样不需要向上继续处理 ,黑色的p隔绝上层,不会再出现双红问题。
原理和子情况1一样,就不演示了
核心特点
- 操作组合:单旋转 + 变色,不能只靠变色解决,旋转是必须步骤;
- 处理结束不向上迭代 :旋转后子树根为黑色的
p,直接隔断上层,不会向上抛双红冲突; - 黑高守恒:旋转 + 变色整套操作不会改变子树所有路径的黑色节点数量;
- 分为两类来源:一类是新节点插入(u=null) ,一类是下层修复向上抛上来的旧节点 (u 存在且黑);
- 构型和 AVL 树 LL/RR 单旋完全一致,区别仅在于红黑树需要额外做颜色修改。
2.4.情况三:双旋+变色(u为null(不存在)或颜色为黑色时)
和情况二的整体前置条件一致,区别仅在于节点相对位置:属于拐弯构型(LR / RL),不能直接单旋。
关键区分与黑高推导
节点来源和情况二完全相同:
- 若
u不存在 :c为本次新插入红色节点。 - 若
u存在且为黑色 :c不是新增节点,原本是黑色,下层触发情况 1 变色,被修改为红色向上传递到此。
同样要注意下层一定是已经修复完成的合法红黑树,各子树黑高是我们颜色调整的判断依据。 该构型直接做单旋无法消除连续红色,也无法保证黑高相等,必须先做一次局部旋转,把拐弯构型转化为情况二的直线构型,再复用情况二的单旋 + 变色逻辑。
处理操作
- 先局部旋转把拐弯构型转为 LL/RR 直线构型,再复用情况二的单旋;
- 最后更新节点颜色,保证全局黑高相等------只记结论就是g变红,c变黑

原理深度分析
此时同样必须让p变黑才能消除连续红色冲突,但 c 与 p、g 形成拐弯(LR/RL),直接对 g 做单旋无法解决结构问题。 处理思路:先局部旋转,把拐弯构型转化为直线构型,之后就完全复用情况二的整套逻辑。
子情况 3‑1:p 是 g 的左孩子,c 是 p 的右孩子(LR 左‑右构型,先左单旋,再右单旋)
第一步:以p为旋转支点执行左单旋 ,将 LR 拐弯构型转化为 LL 直线构型; 经过这一步旋转之后,原来的c变到 p 的位置,此时整个树就变成了情况二的 LL 构型。
第二步:复用情况二逻辑,以g为支点执行右单旋 ; 旋转完成后,c置为黑色,g置为红色。 c成为该子树新的根节点,消除全部连续红色。 处理完毕不需要向上迭代修复 ,子树根是黑色c,隔绝上层,不会产生新的双红冲突。
我们在模拟的时候不用进行俩次旋转,了解旋转的可以知道,对于LR旋转而言,旋转完其实就是c到根,c的左子树给p的右边,c的右子树给g的左边,RL自然就是镜像
下图为完整还原流程

子情况 3‑2:p 是 g 的右孩子,c 是 p 的左孩子(RL 右‑左构型,先右单旋,再左单旋)
第一步:以p为旋转支点执行右单旋,将 RL 拐弯构型转化为 RR 直线构型; 转化完成后,直接复用情况二 RR 的处理逻辑。
第二步:以g作为旋转支点执行左单旋 ; 旋转完成后,c置为黑色,g置为红色。 c成为子树新的根。 同样不需要向上继续处理,黑色根节点隔绝上层,不会产生双红问题。
原理和子情况 3‑1 完全对称,不再重复演示图示。
核心特点
- 操作组合:双旋 + 变色,必须先把拐弯构型转成直线构型,不能直接对 g 做单旋;
- 处理结束不向上迭代 :最终子树根为黑色的
c,隔断上层,不会向上抛双红冲突; - 黑高守恒:整套双旋 + 变色操作,不会改变子树全部路径的黑色节点数量;
- 节点来源和情况二一致:新插入节点(u=null),或是下层更新上抛的旧节点(u 存在且黑);
- 和 AVL 树 LR/RL 双旋步骤完全一致,区别在于红黑树完成旋转之后还要执行颜色修改。
2.5.三种情况插入回顾
3.红黑树的插入代码实现
原理理解了之后,其实插入代码实现起来是很简单的,在AVL树的插入基础上把平衡因子去掉,更新一下节点颜色就可以了,具体实现起来很简单
cpp
template<class K, class V>
bool RBTree<K,V>::insert(const std::pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = Black;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else
return false;
}
cur = new Node(kv);
if (parent->_kv.first > kv.first)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
while (cur->_col == Red && parent->_col == Red)
{
Node* u;
Node* grand = parent->_parent;
if (grand->_left == parent)
{
u = grand->_right;
if (u && u->_col == Red)
{
u->_col = parent->_col = Black;
grand->_col = Red;
cur = grand;
parent = cur->_parent;
}
else
{
if (parent->_left == cur)
{
RotateLL(grand);
grand->_col = Red;
parent->_col = Black;
}
else
{
RotateRR(parent);
RotateLL(grand);
cur->_col = Black;
grand->_col = Red;
}
break;
}
}
else
{
u = grand->_left;
if (u && u->_col == Red)
{
u->_col = parent->_col = Black;
grand->_col = Red;
cur = grand;
parent = cur->_parent;
}
else
{
if (parent->_right == cur)
{
RotateRR(grand);
grand->_col = Red;
parent->_col = Black;
}
else
{
RotateLL(parent);
RotateRR(grand);
cur->_col = Black;
grand->_col = Red;
}
break;
}
}
if (_root == cur)
break;
}
_root->_col = Black;
return true;
}
template<class K, class V>
void RBTree<K, V>::RotateLL(Node* parent)
{
Node* grand = parent->_parent;
Node* subL = parent->_left;
Node* subLR = subL->_right;
if (grand == nullptr)
_root = subL;
else
{
if (grand->_left == parent)
grand->_left = subL;
else
grand->_right = subL;
}
subL->_parent = grand;
subL->_right = parent;
parent->_parent = subL;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
}
template<class K, class V>
void RBTree<K, V>::RotateRR(Node* parent)
{
Node* grand = parent->_parent;
Node* subR = parent->_right;
Node* subRL = subR->_left;
if (grand == nullptr)
_root = subR;
else
{
if (grand->_left == parent)
grand->_left = subR;
else
grand->_right = subR;
}
subR->_parent = grand;
subR->_left = parent;
parent->_parent = subR;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
}
4.红黑树的查找
和二叉搜索树的查找是一样的
cpp
template<class K, class V>
typename RBTree<K, V>::Node* RBTree<K, V>::find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
cur = cur->_right;
else if (cur->_kv.first > key)
cur = cur->_left;
else
return cur;
}
return nullptr;
}
5.红黑树的验证
怎么验证是不是红黑树呢,红黑树最长路径不会超过最短路径 2 倍,那我直接求最长、最短路径,判断 max <= 2*min 不就可以校验红黑树?
这个思路是不可行的。
"最长路径不超过最短路径 2 倍" 是红黑树四条规则推导出来的推论,不是等价条件。就算高度倍数满足,节点颜色依旧可能违反红黑树规则,后续继续插入节点就会直接出错。 因此校验红黑树,必须直接校验四条规则,只要四条规则全部满足,则一定可以保证最长路径不超过最短路径的两倍。
红黑树四条规则校验思路:
- 节点颜色只能是红色或者黑色:使用枚举定义颜色,编译层面就保证不会出现其他非法颜色,无需额外遍历校验。
- 根节点必须是黑色:直接判断根节点颜色即可。
- 不能存在连续的红色节点:采用前序遍历。如果判断当前节点是红色,再去判断左右子节点会比较麻烦,子节点可能为空。换个思路:如果当前节点是红色,则它的父节点一定不能是红色,只需要拿到父节点颜色做判断,不需要访问子节点,代码更加简洁。注意根节点没有父节点,需要做空指针保护。
- 从根到每一条叶子路径,黑色节点的数目必须相等:前序递归遍历,参数传递当前路径累计的黑色节点数量。遇到黑色真实节点就将计数 + 1;递归走到空节点代表一条路径结束。取第一条路径的黑色节点数作为基准值,其余所有路径都和基准对比,如果出现不相等,说明黑色平衡被破坏。
代码如下
cpp
template<class K, class V>
bool RBTree<K, V>::isRBTree()
{
if (_root == nullptr)
return true;
if (_root->_col == Red)
return false;
int Blen = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == Black)
Blen++;
cur = cur->_left;
}
return _isRBTree(_root, 0, Blen);
}
template<class K, class V>
bool RBTree<K, V>::_isRBTree(Node* root,int tmp,int Blen)
{
if (root == nullptr)
{
if (tmp != Blen)
{
std::cout << "黑高异常" << std::endl;
return false;
}
return true;
}
if (root->_col == Red && root->_parent->_col == Red)
{
std::cout << "双红节点" << std::endl;
return false;
}
if (root->_col == Black)
tmp++;
return _isRBTree(root->_left, tmp, Blen) && _isRBTree(root->_right, tmp, Blen);
}
测试代码
cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Red Black Tree.h"
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
#if 1
// 测试代码
void TestRBTree1()
{
RBTree<int, int> t;
// 常规的测试用例
//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
// 特殊的带有双旋场景的测试用例
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
t.insert({ e, e });
}
t.InOrder();
cout << t.isRBTree() << endl;
}
// 插入一堆随机值,测试平衡,顺便测试一下高度和性能等
void TestRBTree2()
{
const int N = 100000;
vector<int> v;
v.reserve(N);
srand((unsigned int)time(0));
for (size_t i = 0; i < N; i++)
{
v.push_back(rand() + i);
}
size_t begin2 = clock();
RBTree<int, int> t;
for (auto e : v)
{
t.insert(make_pair(e, e));
}
size_t end2 = clock();
cout << "Insert:" << end2 - begin2 << endl;
cout << t.isRBTree() << endl;
size_t begin1 = clock();
// 确定在的值
/*for (auto e : v)
{
t.Find(e);
}*/
// 随机值
for (size_t i = 0; i < N; i++)
{
t.find((rand() + i));
}
size_t end1 = clock();
cout << "Find:" << end1 - begin1 << endl;
}
#endif
int main()
{
//TestRBTree1();
TestRBTree2();
return 0;
}
6.红黑树的删除
红黑树删除逻辑复杂度远高于插入,分支场景繁多,还存在黑色权重向上传播、兄弟节点借黑、变色补偿等复杂逻辑。 对于学习阶段,优先吃透插入,理解红黑树核心思想:黑高守恒、冲突向上迭代、旋转配合颜色调整,就已经掌握红黑树绝大部分精髓。 面试当中,一般只要求口述删除各个场景,很少要求完整手写实现,本文就不再实现删除部分。如果后续有机会,再单独补充删除章节。
结语:
用C++实现的高阶数据结构的红黑树部分的内容就全部讲解完毕啦,希望以上内容对你有所帮助,感谢观看,若觉得写的还可以,可以分享给朋友一起来看哦,毕竟一起进步更有动力嘛,当然能关注一下就更好啦。


