跳到主要内容
C++ 红黑树详解:原理、操作与代码实现 | 极客日志
C++ 算法
C++ 红黑树详解:原理、操作与代码实现 综述由AI生成 系统讲解了 C++ STL 红黑树的核心知识。涵盖红黑树定义、四大特性、时间复杂度分析,重点解析插入操作的三种调整场景(变色、单旋、双旋)及验证逻辑。提供完整 C++ 类模板实现,包含查找、插入、旋转、平衡校验等功能,并通过百万级数据规模测试对比了红黑树与 AVL 树的性能差异。
FlinkHero 发布于 2026/3/25 更新于 2026/4/26 14 浏览概念介绍
1. 什么是红黑树?
红黑树 :是一种自平衡二叉搜索树 ,由德国计算机科学家 Rudolf Bayer 在 1972 年发明,当时被称为对称二叉 B 树,后来被 Leo J. Guibas 和 Robert Sedgewick 改进并命名为红黑树。它在很多编程语言的库和实际应用场景中都有广泛使用,如 C++ 的 STL 库中的 map 和 set 底层实现、Java 的 TreeMap 等库的实现。
它通过额外的 颜色标记 和 旋转操作 来维持树的平衡,确保最坏情况下的基本操作(插入、删除、查找)时间复杂度为 O(log n)。它在每个节点上增加了一个存储位来表示节点的颜色,这个颜色可以是红色或者黑色。通过对从根节点到任意叶子节点路径上的节点颜色进行约束,红黑树确保了没有一条路径的长度会比其他路径长出两倍,因此它是一种近似平衡的二叉搜索树。
2. 红黑树的基本特性是什么?
红黑树的基本特性:
节点颜色属性 :红黑树的每个节点都有一个颜色属性,颜色只能是红色或者黑色。
根节点与叶子节点 :根节点是黑色的;叶子节点(NIL 节点,这里可以理解为指向空的指针,并不是我们通常意义上的叶子节点)每个都是黑色的。
红节点规则 :如果一个节点是红色的,那么它的两个子节点都是黑色的,也就是说不存在连续的红色节点。
黑色高度 :对每个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点。这个相同的黑色节点数目被称为该节点的'黑色高度',整棵红黑树的黑色高度就是根节点的黑色高度。
3. 红黑树的效率怎么样?
红黑树的优点:
对于二叉搜索树而言:时间效率高 :红黑树能保证在最坏情况下,基本的查找、插入、删除操作的时间复杂度都是 O(log N),其中 N 是树中节点的数量。假设红黑树中结点数量为 N,最短路径长度为 h,则满足 2^h - 1 ≤ N < 2^(2h) - 1。由此可推出 h ≈ log N,这意味着红黑树增删查改操作的最坏情况,是走最长路径 2 × log N,所以时间复杂度仍为 O(log N)。相比普通的二叉搜索树,普通二叉搜索树在极端情况下(如:插入节点有序时)会退化为链表,导致操作时间复杂度变为 O(n),而红黑树通过平衡机制避免了这种情况。
对于 AVL 树而言:稳定性较好 :红黑树的平衡调整操作相对比较稳定,虽然在插入和删除节点时会进行旋转和变色,但这些操作的代价相对较小,不会引起树结构的剧烈变化。
总结: 红黑树的概念表达相对 AVL 树更抽象。AVL 树通过高度差直观控制平衡,红黑树则借助 4 条颜色约束规则,间接实现近似平衡。二者效率处于同一档次,但相对而言,插入相同数量结点时,红黑树的旋转次数更少,因为它对平衡的控制没那么严格。
4. 红黑树如何确保最长路径不超过最短路径的 2 倍?
红黑树路径规则推导:
最短路径(全黑路径) :由红黑树规则 4(从根到 NIL 结点的每条路径,黑色结点数量相同)可知,极端场景下,最短路径就是全由黑色结点组成 的路径。我们把这条最短路径的长度记为 bh(black height,黑色高度)。
最长路径(黑红间隔路径) :结合规则 2(根结点是黑色)和规则 3(红色结点的子结点必为黑色,路径中不会出现连续红色结点)。极端场景下,最长路径会呈现 的结构,因此最长路径长度为 (黑色结点数量为 ,红色结点数量最多也为 )。
一黑一红交替
2*bh
bh
bh
路径长度范围 :实际红黑树中,'全黑最短路径'和'黑红交替最长路径'并非一定同时存在。但通过 4 条规则可推导:任意从根到 NIL 结点的路径长度 h ,都满足**bh ≤ h ≤ 2*bh**,这保证了红黑树的近似平衡特性。
基本操作
一、查找操作
查找操作总结:
从二叉搜索树,到 AVL 树,再到红黑树,随着学习逐步深入,我们会发现:这三种树的查找操作,在核心逻辑上高度一致。
它们都依托二叉搜索树的'左小右大'基本规则,从根节点出发,通过键值的比较,不断向左右子树递归或迭代查找,本质都是利用树的有序性缩小查找范围。差异主要体现在平衡维护(AVL 树的严格平衡、红黑树的近似平衡),但查找操作的'二分比较'形式,始终保持着一脉相承的简洁与高效。
二、插入操作
1. 本质
插入操作本质 :
红黑树的插入操作是在二叉搜索树的基础上,通过颜色调整 和旋转操作 来维持树的近似平衡特性。
2. 步骤
场景说明:
在红黑树插入调整逻辑里,我们统一做如下标识:新插入的节点记为 c(current,代表当前触发调整的节点);c 的父节点记为 p(parent);p 的父节点(即:祖父节点)记为 g(grandfather);p 的兄弟节点(即:叔叔节点)记为 u(uncle)
后续将基于这些标识,分析不同场景下的红黑树平衡调整规则。
情况 1:变色
情况 1 :当新插入节点 c 为红色、父节点 p 为红色(此时因红黑树性质,祖父节点 g 必为黑色),且叔叔节点 u 存在且为红色 时:
颜色调整 :将 p 和 u 染为黑色,g 染为红色。
向上回溯 :把 g 视为新的'当前节点',继续向上检查调整。
原理分析:
黑色高度守恒 :原本 p(红)、u(红)、g(黑)的结构里,g 子树的黑色节点数由 g 维持。调整后 p、u 变黑(为子树各加一个黑节点),g 变红(抵消自身原本的黑色贡献),整体黑色高度不变。
解决连续红节点 :c 与 p 的连续红色问题,因 p 变黑得以解决。
继续回溯的必要性 :g 变红后,若它的父节点是红色,会形成新的'连续红节点'违规,需继续向上处理;若父节点是黑色,当前路径恢复合规,无需调整;若 g 已是整棵树的根,为满足'根节点必黑'规则,要再把 g 染回黑色收尾。
**红黑树平衡处理的图示说明:类似 AVL 树的场景化分析思路,上面图片展示的是红黑树的插入情况 1:变色的是一种具体案例,但实际红黑树需要处理的平衡场景远不止这一种。下面的图片中我们将对这类平衡逻辑做了 抽象归纳 :用 d/e/f 代表'路径含 hb 个黑色节点的子树';a/b 代表'路径含 hb-1 个黑色节点且根为红色的子树'(其中 hb ≥ 0,是对黑色节点数量的抽象度量)。
下面的三种图片则分别对应 hb = 0、hb = 1、hb = 2 时的具体场景组合分析。尤其当 hb = 2 时,理论上的组合情况可达上百亿种,但核心逻辑始终一致:无论场景多复杂,均通过 变色 + 向上回溯调整 即可解决。因此 :我们无需逐一分析极端案例,理解上图的抽象模型,就能掌握这类平衡问题的通用解法。
图示 1 :红黑树的插入情况 1:变色 ---> '插入前 a/b/d/e/f 的黑色高度 bh==0'
图示 2 :红黑树的插入情况 1:变色 ---> '插入前 a/b/d/e/f 的黑色高度 bh==1'
图示 3 :红黑树的插入情况 1:变色 ---> '插入前 a/b/d/e/f 的黑色高度 bh==2'
情况 2:变色 + 单旋
情况 2 :当新节点 c 为红色、父节点 p 为红色(此时祖父 g 必为黑色),且叔叔节点 u 不存在,或 u 存在但为黑色 时:
若叔叔节点 u 不存在 → c 一定是本次新增的节点 (因原树满足红黑性质,无连续红节点)。
若叔叔节点 u 存在且为黑色 → c不是新增节点 (它原本是黑色,因子树插入触发调整,从黑色变为红色后'上升'到当前位置)。
核心矛盾 :此时仅靠变色无法解决连续红节点问题 (p 为红,若不变黑仍会违规),需结合旋转 + 变色 调整。
◼ 场景 A:p 是 g 的左孩子,c 是 p 的左孩子(左左型)
操作 :以 g 为旋转中心右单旋。旋转后,p 染黑,g 染红。
效果 :p 成为新子树的根,黑色节点数量不变(满足'路径黑色高度一致'),消除 c 与 p 的连续红节点问题(p 变黑)。无需继续向上调整:因 p 的父节点(原 g 的父节点)无论颜色如何,都不会触发新的连续红违规。
◼ 场景 B:p 是 g 的右孩子,c 是 p 的右孩子(右右型)
操作 :以 g 为旋转中心左单旋。旋转后,p 染黑,g 染红。
效果 :p 成为新子树的根,黑色节点数量不变,消除 c 与 p 的连续红节点问题。无需继续向上调整:同理,p 新的父节点不触发违规。
图示 1 :红黑树的插入情况 2:变色 + 单旋 ---> '具体情况'
图示 2 :红黑树的插入情况 2:变色 + 单旋 ---> '抽象情况'
图示 3 :红黑树的插入情况 2:变色 + 单旋 ---> '插入前 a/b/d/e/f 的黑色高度 bh==1'
情况 3:变色 + 双旋
当新节点 c 为红色、父节点 p 为红色(祖父 g 必为黑色),且叔叔节点 u 不存在,或 u 存在但为黑色 时:
若叔叔节点 u 不存在 → c 是本次新增节点 (原树无连续红节点,符合插入规则)。
若叔叔节点 u 存在且为黑色 → c不是新增节点 (它原本为黑色,因下层子树插入触发调整,颜色变为红色后'上升'到当前层级)。
核心矛盾 :此时仅靠变色无法修复连续红节点问题 (p 为红色,必须变黑才能解决违规),但因 u 为黑色 / 不存在,直接变色会破坏'黑色高度一致'规则,需通过双旋转 + 变色 调整。
◼ 场景 1:p 是 g 的左孩子,c 是 p 的右孩子(左右型)
操作 :以 p 为旋转中心,左单旋 (将 c 提升为 p 的父节点);以 g 为旋转中心,右单旋 (将 c 提升为 g 的父节点);变色:c 染黑,g 染红。
效果 :c 成为新子树的根,黑色节点数量不变(满足'路径黑色高度一致'),消除连续红节点问题(p 与 g 不再连续为红)。无需继续向上调整:因 c 的父节点(原 g 的父节点)无论颜色如何,都不会触发新的连续红违规。
◼ 场景 2:p 是 g 的右孩子,c 是 p 的左孩子(右左型)
操作 :以 p 为旋转中心,右单旋 (将 c 提升为 p 的父节点);以 g 为旋转中心,左单旋 (将 c 提升为 g 的父节点);变色:c 染黑,g 染红。
效果 :c 成为新子树的根,黑色节点数量不变,消除连续红节点问题。无需继续向上调整:同理,c 新的父节点不触发违规。
图示 1 :红黑树的插入情况 3:变色 + 双旋 ---> '具体情况'
图示 2 :红黑树的插入情况 3:变色 + 双旋 ---> '抽象情况'
图示 3 :红黑树的插入情况 3:变色 + 双旋 ---> '插入前 a/b/d 的黑色高度 bh==1'
三、验证操作
红黑树规则校验的正确思路:
直接通过最长路径与最短路径的倍数关系 (如:最长路径≤最短路径的 2 倍)来验证红黑树是不可行的 。因为即使满足该条件,树的颜色规则也可能违规,且当前无问题的树,后续插入新节点时仍可能因颜色规则破坏而失衡。
因此 :正确的做法是直接校验红黑树的 4 条核心规则 —— 只要满足这 4 条,自然能保证'最长路径≤最短路径的 2 倍'。
具体校验逻辑如下:
规则 1:颜色合法性
通过枚举定义颜色类型(仅黑色、红色),天然保证节点颜色合法,无需额外校验。
规则 2:根节点颜色
直接检查根节点是否为黑色即可,逻辑简单。
规则 3:红色节点的子节点合法性
直接思路 :若直接遍历检查'红色节点的子节点是否为黑色',需处理子节点不存在的情况,较为繁琐。
优化思路 :反向校验 —— 遍历节点时,检查当前节点的父节点颜色。若父节点为红色且当前节点也为红色,即违反'红色节点的子节点必为黑色'规则。
规则 4:路径黑色节点数量一致性
通过前序遍历 + 传递黑色节点计数 实现:遍历过程中,用参数 blackNum 记录'从根到当前节点的黑色节点数量'。遇到黑色节点时,blackNum++;遇到空节点(路径终点)时,记录当前路径的黑色节点总数。以第一条路径的黑色节点数量为'参考值',后续遍历其他路径时逐一对比。若所有路径的黑色节点数量与参考值一致,则满足规则 4。
逻辑总结:
校验红黑树时,直接通过'最长/最短 路径倍数'判断不可靠,需严格校验 4 条核心规则:
规则 1:靠枚举天然保证
规则 2:直接检查根
规则 3:反向校验父节点颜色更高效
规则 4:通过前序遍历 + 计数对比实现
满足这 4 条规则,红黑树的'最长路径≤最短路径 2 倍'特性会被自动保证 ,无需额外验证。
图示 :'验证一棵树是不是红黑树'
bool IsRBTree () {
if (_root == nullptr ) {
return true ;
}
if (_root->_col == RED) {
return false ;
}
int refNum = 0 ;
Node* current = _root;
while (current) {
if (current->_col == BLACK) {
++refNum;
}
current = current->_left;
}
return Check (_root, 0 , refNum);
}
bool Check (Node* root, int blackNum, const int refNum) {
if (root == nullptr ) {
if (refNum != blackNum) {
cout << "存在黑色结点的数量不相等的路径" << endl;
return false ;
}
return true ;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << " 存在连续的红色结点" << endl;
return false ;
}
if (root->_col == BLACK) {
blackNum++;
}
return Check (root->_left, blackNum, refNum) && Check (root->_right, blackNum, refNum);
}
代码实现
红黑树的存储结构是什么样的?
一、节点的存储结构
enum Colour {
RED,
BLACK
};
template <class K , class V >
struct RBTreeNode {
pair<K, V> _kv;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Colour _col;
RBTreeNode (const pair<K, V>& kv)
: _kv(kv), _left(nullptr ), _right(nullptr ), _parent(nullptr ) {}
};
二、树的存储结构
template <class K , class V >
class RBTree {
private :
RBTreeNode<K, V>* _root = nullptr ;
typedef RBTreeNode<K, V> Node;
public :
};
实现文件:RBTree.h #pragma once
#include <iostream>
using namespace std;
enum Colour {
RED,
BLACK
};
template <class K , class V >
struct RBTreeNode {
pair<K, V> _kv;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Colour _col;
RBTreeNode (const pair<K, V>& kv)
: _kv(kv), _left(nullptr ), _right(nullptr ), _parent(nullptr ) {}
};
template <class K , class V >
class RBTree {
private :
RBTreeNode<K, V>* _root = nullptr ;
typedef RBTreeNode<K, V> Node;
void _InOrder(Node* root) {
if (root == nullptr ) {
return ;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root) {
if (root == nullptr ) {
return 0 ;
}
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1 ;
}
int _Size(Node* root) {
if (root == nullptr ) {
return 0 ;
}
return _Size(root->_left) + _Size(root->_right) + 1 ;
}
bool Check (Node* root, int blackNum, const int refNum) {
if (root == nullptr ) {
if (refNum != blackNum) {
cout << "存在黑色结点的数量不相等的路径" << endl;
return false ;
}
return true ;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << " 存在连续的红色结点" << endl;
return false ;
}
if (root->_col == BLACK) {
blackNum++;
}
return Check (root->_left, blackNum, refNum) && Check (root->_right, blackNum, refNum);
}
public :
Node* Find (const K& key)
{
Node* curr = _root;
while (curr) {
if (curr->_kv.first < key) {
curr = curr->_right;
}
else if (curr->_kv.first > key) {
curr = curr->_left;
}
else {
return curr;
}
}
return nullptr ;
}
bool Insert (const pair<K, V>& kv)
{
if (_root == nullptr ) {
_root = new Node (kv);
_root->_col = BLACK;
return true ;
}
Node* current = _root;
Node* parent = nullptr ;
while (current)
{
if (current->_kv.first < kv.first)
{
parent = current;
current = current->_right;
}
else if (current->_kv.first > kv.first)
{
parent = current;
current = current->_left;
}
else {
return false ;
}
}
current = new Node (kv);
current->_col = RED;
if (kv.first < parent->_kv.first)
{
parent->_left = current;
}
else
{
parent->_right = current;
}
current->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left) {
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
current = grandfather;
parent = grandfather->_parent;
}
else {
if (current == parent->_left) {
RotateR (grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else {
RotateL (parent);
RotateR (grandfather);
current->_col = BLACK;
grandfather->_col = RED;
}
break ;
}
}
else {
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
current = grandfather;
parent = grandfather->_parent;
}
else {
if (current == parent->_right) {
RotateL (grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else {
RotateR (parent);
RotateL (grandfather);
current->_col = BLACK;
grandfather->_col = RED;
}
break ;
}
}
}
_root->_col = BLACK;
return true ;
}
void RotateR (Node* parent) {
Node* subL = parent->_left;
Node* subLR = parent->_left->_right;
Node* pParent = parent->_parent;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
parent->_parent = subL;
subL->_right = parent;
if (parent == _root) {
_root = subL;
subL->_parent = nullptr ;
}
else {
if (parent == pParent->_left) {
pParent->_left = subL;
} else {
pParent->_right = subL;
}
subL->_parent = pParent;
}
}
void RotateL (Node* parent) {
Node* subR = parent->_right;
Node* subRL = parent->_right->_left;
Node* pParent = parent->_parent;
parent->_right = subRL;
if (subRL) {
subRL->_parent = parent;
}
parent->_parent = subR;
subR->_left = parent;
if (pParent == nullptr )
{
_root = subR;
subR->_parent = nullptr ;
}
else {
if (parent == pParent->_left) {
pParent->_left = subR;
} else {
pParent->_right = subR;
}
subR->_parent = pParent;
}
}
void InOrder () {
_InOrder(_root);
cout << endl;
}
int Height () {
return _Height(_root);
}
int Size () {
return _Size(_root);
}
bool IsRBTree () {
if (_root == nullptr ) {
return true ;
}
if (_root->_col == RED) {
return false ;
}
int refNum = 0 ;
Node* current = _root;
while (current) {
if (current->_col == BLACK) {
++refNum;
}
current = current->_left;
}
return Check (_root, 0 , refNum);
}
};
测试文件:Test.cpp #define _CRT_SECURE_NO_WARNINGS 1
#include "RBTree.h"
void TestRBTree () {
RBTree<int , int > rbTree;
int a[] = { 4 , 2 , 6 , 1 , 3 , 5 , 15 , 7 , 16 , 14 };
for (auto e : a) {
rbTree.Insert ({ e, e });
}
std::cout << "中序遍历结果:" << std::endl;
rbTree.InOrder ();
std::cout << "红黑树平衡性验证结果:" << (rbTree.IsRBTree () ? "平衡(1)" : "不平衡(0)" ) << std::endl;
int keyToFind = 7 ;
auto foundNode = rbTree.Find (keyToFind);
if (foundNode) {
std::cout << "找到节点:" << foundNode->_kv.first << ":" << foundNode->_kv.second << std::endl;
} else {
std::cout << "未找到节点:" << keyToFind << std::endl;
}
std::cout << "树的高度:" << rbTree.Height () << std::endl;
std::cout << "节点数量:" << rbTree.Size () << std::endl;
}
int main () {
TestRBTree ();
return 0 ;
}
运行结果:
终极对决
一、选手登场
AVL 树的源代码 #pragma once
#include <iostream>
#include <assert.h>
using namespace std;
template <class K , class V >
struct AVLTreeNode {
pair<K, V> _kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;
AVLTreeNode (const pair<K, V>& kv)
: _kv(kv), _left(nullptr ), _right(nullptr ), _parent(nullptr ), _bf(0 ) {}
};
template <class K , class V >
class AVLTree {
private :
AVLTreeNode<K, V>* _root = nullptr ;
typedef AVLTreeNode<K, V> Node;
void _InOrder(Node* root) {
if (root == nullptr ) {
return ;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root) {
if (root == nullptr ) {
return 0 ;
}
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return (leftHeight > rightHeight) ? leftHeight + 1 : rightHeight + 1 ;
}
int _Size(Node* root) {
if (root == nullptr ) {
return 0 ;
}
return _Size(root->_left) + _Size(root->_right) + 1 ;
}
bool _IsAVLTree(Node* root) {
if (root == nullptr ) {
return true ;
}
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int bf = rightHeight - leftHeight;
if (abs (bf) >= 2 ) {
cout << root->_kv.first << "平衡因子不合法" << endl;
return false ;
}
if (root->_bf != bf) {
cout << root->_kv.first << "平衡因子异常" << endl;
return false ;
}
return _IsAVLTree(root->_left) && _IsAVLTree(root->_right);
}
public :
Node* Find (const K& key) {
Node* curr = _root;
while (curr) {
if (curr->_kv.first < key) {
curr = curr->_right;
}
else if (curr->_kv.first > key) {
curr = curr->_left;
}
else {
return curr;
}
}
return nullptr ;
}
bool Insert (const pair<K, V>& kv)
{
if (_root == nullptr ) {
_root = new Node (kv);
return true ;
}
Node* current = _root;
Node* parent = nullptr ;
while (current)
{
if (current->_kv.first < kv.first)
{
parent = current;
current = current->_right;
}
else if (current->_kv.first > kv.first)
{
parent = current;
current = current->_left;
}
else {
return false ;
}
}
current = new Node (kv);
if (kv.first < parent->_kv.first)
{
parent->_left = current;
}
else
{
parent->_right = current;
}
current->_parent = parent;
while (parent)
{
if (current == parent->_left) {
parent->_bf--;
}
else {
parent->_bf++;
}
if (parent->_bf == 0 ) {
break ;
}
else if (parent->_bf == -1 || parent->_bf == 1 ) {
current = current->_parent;
parent = parent->_parent;
}
else if (parent->_bf == -2 || parent->_bf == 2 )
{
if (parent->_bf == -2 && current->_bf == -1 ) {
RotateR (parent);
}
else if (parent->_bf == 2 && current->_bf == 1 ) {
RotateL (parent);
}
else if (parent->_bf == -2 && current->_bf == 1 ) {
RotateLR (parent);
}
else if (parent->_bf == 2 && current->_bf == -1 ) {
RotateRL (parent);
}
else {
assert (false );
}
break ;
}
else {
assert (false );
}
}
return true ;
}
void RotateR (Node* parent) {
Node* subL = parent->_left;
Node* subLR = parent->_left->_right;
Node* pParent = parent->_parent;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
parent->_parent = subL;
subL->_right = parent;
if (parent == _root) {
_root = subL;
subL->_parent = nullptr ;
}
else {
if (parent == pParent->_left) {
pParent->_left = subL;
} else {
pParent->_right = subL;
}
subL->_parent = pParent;
}
subL->_bf = 0 ;
parent->_bf = 0 ;
}
void RotateL (Node* parent) {
Node* subR = parent->_right;
Node* subRL = parent->_right->_left;
Node* pParent = parent->_parent;
parent->_right = subRL;
if (subRL) {
subRL->_parent = parent;
}
parent->_parent = subR;
subR->_left = parent;
if (pParent == nullptr )
{
_root = subR;
subR->_parent = nullptr ;
}
else {
if (parent == pParent->_left) {
pParent->_left = subR;
} else {
pParent->_right = subR;
}
subR->_parent = pParent;
}
subR->_bf = 0 ;
parent->_bf = 0 ;
}
void RotateLR (Node* parent) {
Node* subL = parent->_left;
Node* subLR = parent->_left->_right;
int bf = subLR->_bf;
RotateL (parent->_left);
RotateR (parent);
if (bf == -1 ) {
subLR->_bf = 0 ;
subL->_bf = 0 ;
parent->_bf = 1 ;
}
else if (bf == 1 ) {
subLR->_bf = 0 ;
subL->_bf = -1 ;
parent->_bf = 0 ;
}
else if (bf == 0 )
{
subLR->_bf = 0 ;
subL->_bf = 0 ;
parent->_bf = 0 ;
}
else {
assert (false );
}
}
void RotateRL (Node* parent) {
Node* subR = parent->_right;
Node* subRL = parent->_right->_left;
int bf = subRL->_bf;
RotateR (parent->_right);
RotateL (parent);
if (bf == -1 ) {
subRL->_bf = 0 ;
subR->_bf = 1 ;
parent->_bf = 0 ;
}
else if (bf == 1 ) {
subRL->_bf = 0 ;
subR->_bf = 0 ;
parent->_bf = -1 ;
}
else if (bf == 0 ) {
subRL->_bf = 0 ;
subR->_bf = 0 ;
parent->_bf = 0 ;
}
else {
assert (false );
}
}
void InOrder () {
_InOrder(_root);
cout << endl;
}
int Height () {
return _Height(_root);
}
int Size () {
return _Size(_root);
}
bool IsAVLTree () {
return _IsAVLTree(_root);
}
};
红黑树的源代码
二、比赛开始
测试的源文件:Test.cpp
#include "AVLTree.h"
#include "RBTree.h"
#include <vector>
#include <ctime>
using namespace std;
void TestBST () {
cout << "测试一百万的数据规模下 AVL 树和红黑树的性能差距 \n" << endl;
const int N = 1000000 ;
vector<int > v;
v.reserve (N);
srand (time (0 ));
for (size_t i = 0 ; i < N; i++) {
v.push_back (rand () + i);
}
AVLTree<int , int > avl;
size_t begin1 = clock ();
for (auto it : v) {
avl.Insert (make_pair (it, it));
}
size_t end1 = clock ();
RBTree<int , int > rb;
size_t begin2 = clock ();
for (auto it : v) {
rb.Insert (make_pair (it, it));
}
size_t end2 = clock ();
size_t begin3 = clock ();
for (auto it : v) {
avl.Find (it);
}
size_t end3 = clock ();
size_t begin4 = clock ();
for (auto it : v) {
rb.Find (it);
}
size_t end4 = clock ();
cout << "-----------插入操作的耗时-----------" << endl;
cout << "AVL Insert:" << end1 - begin1 << endl;
cout << "RB Insert:" << end2 - begin2 << endl;
cout << "\n-----------查找操作的耗时-----------" << endl;
cout << "AVL Find:" << end3 - begin3 << endl;
cout << "RB Find:" << end4 - begin4 << endl;
cout << "\n-----------是否平衡-----------" << endl;
cout << "AVL IsBalance:" << avl.IsAVLTree () << endl;
cout << "RB IsBalance:" << rb.IsRBTree () << endl;
cout << "\n-----------树的高度-----------" << endl;
cout << "AVL Height:" << avl.Height () << endl;
cout << "RB Height:" << rb.Height () << endl;
cout << "\n-----------插入节点的数量-----------" << endl;
cout << "AVL Size:" << avl.Size () << endl;
cout << "RB Size:" << rb.Size () << endl;
}
int main () {
TestBST ();
return 0 ;
}
三、评委打分
运行结果截图 相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online