【C++进阶】红黑树
目录
- 什么是红黑树?
- 红黑树
- 红黑树的性质
- 定义红黑树
- 红黑树的操作
- insert
- inorder
- find
- height
- size
- 构造函数
- 析构函数
- 赋值拷贝
- 判断红黑树
- 全部代码
- 总结
什么是红黑树?
红黑树
红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,用于保持树的平衡,以确保在最坏情况下基本操作(如插入、删除和查找)的时间复杂度仍为 O(log n)。红黑树的每个节点都包含一个额外的颜色位,即红色或黑色。红黑树通过严格的平衡条件来维持树的平衡。
红黑树的性质
- 节点是红色或黑色的:每个节点都有颜色属性,红色或黑色。
- 根是黑色的:红黑树的根节点必须是黑色的。
- 叶节点(NIL 节点)是黑色的:红黑树中的所有叶节点(这里的叶节点是指为空的 NIL 节点,而不是实际的元素节点)都是黑色的。
- 红色节点的子节点必须是黑色的(即红色节点不能有红色子节点,也叫“红黑相间”):红色节点不能连续连接在一起。
- 从任何节点到其每个叶子的所有简单路径都必须包含相同数量的黑色节点:这确保了树的平衡性。
定义红黑树
enum Color
{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;Color _col;RBTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr){}
};
template<class K,class V>
class RBTree
{
public:typedef RBTreeNode<K, V> Node;
private:Node* _root = nullptr;
};
红黑树的操作
insert
红黑树的逻辑是在普通的二叉搜索树上进行实现的,在普通的二叉搜索树中,我们不需要调节平衡,但是在红黑树当中我们需要根据红黑树的性质来调整数的颜色和高度,首先我们来看看第一种情况:

cur是插入节点,由于红色节点不能连续,所以这里已经破坏了平衡,所以我们应该进行调整颜色,我们不可能插入cur是黑色节点,因为如果插入黑色节点的话代价很大,每条路上都会多出来一个黑色节点,这样很不好维护,所以插入红色节点是代价最小的,插入红色节点之后,破坏了规则,所以p应该变成黑色,由于p这条路多了一个黑色节点,所以u应该也变成黑色节点,由于这棵树有可能是某个子树,这两条路多出来一个黑色节点为了保证黑色节点不多,所以g节点应该也变成红色,这样做完之后继续往上调整,将cur赋值到g节点,继续往上调整颜色。

这样就算调整完毕了。
第二种情况:当u节点不存在时

对于这种情况我们先准备调色,首先我们先将p改为黑色节点,然后将g改为红色节点之后,我们会发现,左边这条路多出来一个黑色节点,这样是不行的。

上面这个是不行的,左边已经多出来了一个黑色节点了,但是右边没有黑色节点,所以这种情况我们应该进行旋转。
对g节点进行右旋:

进行右旋之后将p改为黑色节点,将g改为红色节点,这样两边都保持平衡了。

还有一种情况:u存在但是为黑色

最下面的那个红色节点是插入节点:
这种情况我们先向上调整:

调整成这样之后,我们就发现,u是黑色节点,不管我们怎么调整,左边都会多出一个黑色节点,所以这里我们应该旋转:
绕着g节点进行旋转之后我们可以发现调整颜色就会平衡:

还有双旋的情况,双旋的情况,我们只说一种:

左旋将p和cur的位置交换,然后再对g进行右旋,就可以达到平衡的效果。
左旋接口:
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left)parentParent->_left = subR;else parentParent->_right = subR;subR->_parent = parentParent;}
}
右旋接口:
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent)parentParent->_left = subL;else parentParent->_right = subL;subL->_parent = parentParent;}
}
insert的代码:
bool Insert(const 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->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else return false;}cur = new Node(kv);cur->_col = RED;//插入到右边if (parent->_kv.first < kv.first)parent->_right = cur;else parent->_left = cur;cur->_parent = parent;//调整颜色或旋转//parent不为空并且parent指向的颜色是红色才进行调整while (parent && parent->_col == RED){Node* grandparent = parent->_parent;if (grandparent->_left == parent){Node* uncle = grandparent->_right;//uncle存在且是红色if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}//uncle不存在或者存在为黑色else{//判断cur是parent的左子树还是右子树://右单旋if (cur == parent->_left){RotateR(grandparent);grandparent->_col = RED;parent->_col = BLACK;}else{RotateL(parent);RotateR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}else{Node* uncle = grandparent->_left;//uncle存在且是红色if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}//uncle不存在或者存在为黑色else{//判断cur是parent的左子树还是右子树://右单旋if (cur == parent->_right){RotateL(grandparent);grandparent->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}_root->_col = BLACK;return true;
}
inorder
void InOrder()
{_InOrder(_root);
}
void _InOrder(Node* root)
{if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << ':' << root->_kv.second << endl;_InOrder(root->_right);
}
find
bool Find(const K& k)
{Node* cur = _root;while (cur){if (cur->_kv.first < k) cur = cur->_right;else if (cur->_kv.first > k) cur = cur->_left;else return true;}return false;
}
height
int height()
{return _height(_root);
}
int _height(Node* root)
{if (root == nullptr)return 0;int leftheight = _height(root->_left);int rightheight = _height(root->_right);return max(leftheight, rightheight) + 1;
}
size
int size()
{return _size(_root);
}
int _size(Node* root)
{if (root == nullptr)return 0;int left = _size(root->_left);int right = _size(root->_right);return left + right + 1;
}
构造函数
//默认构造
RBTree() = default;
//拷贝构造
RBTree(const RBTree<K, V>& t)
{_root = Copy(t._root);
}
Node* Copy(Node* root)
{if (root == nullptr)return nullptr;Node* newnode = new Node(root->_kv);newnode->_left = Copy(root->_left);newnode->_right = Copy(root->_right);return newnode;
}
析构函数
~RBTree()
{Destroy(_root);_root = nullptr;
}
void Destroy(Node* root)
{if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;
}
赋值拷贝
RBTree<K, V>& operator=(RBTree<K, V> t)
{swap(_root, t._root);return *this;
}
判断红黑树
bool IsBalance()
{//空树也是搜索树if (_root == nullptr) return true;//根节点不能为红色if (_root->_col == RED) return false;//参考值int refNum = 0;Node* cur = _root;while (cur){//遇到黑色++if (cur->_col == BLACK) refNum++;cur = cur->_left;}return Check(_root, 0, refNum);
}
bool Check(Node* root, int blackNum, const int refNum)
{//走到空之后就算出一条路径的数量了if (root == nullptr){if (blackNum != refNum){cout << "存在黑色节点不相等的路径" << endl;return false;}return true;}//存在连续的红色节点if (root->_col == RED && root->_parent->_col == RED)return false;if (root->_col == BLACK)blackNum++;return Check(root->_left, blackNum, refNum) && Check(root->_right, blackNum, refNum);
}
全部代码
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
enum Color
{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;Color _col;RBTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr){}
};
template<class K,class V>
class RBTree
{
public:typedef RBTreeNode<K, V> Node;//默认构造RBTree() = default;//拷贝构造RBTree(const RBTree<K, V>& t){_root = Copy(t._root);}//赋值拷贝RBTree<K, V>& operator=(RBTree<K, V> t){swap(_root, t._root);return *this;}//析构函数~RBTree(){Destroy(_root);_root = nullptr;}//查找bool Find(const K& k){Node* cur = _root;while (cur){if (cur->_kv.first < k) cur = cur->_right;else if (cur->_kv.first > k) cur = cur->_left;else return true;}return false;}//中序遍历void InOrder(){_InOrder(_root);}//插入bool Insert(const 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->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else return false;}cur = new Node(kv);cur->_col = RED;//插入到右边if (parent->_kv.first < kv.first)parent->_right = cur;else parent->_left = cur;cur->_parent = parent;//调整颜色或旋转//parent不为空并且parent指向的颜色是红色才进行调整while (parent && parent->_col == RED){Node* grandparent = parent->_parent;if (grandparent->_left == parent){Node* uncle = grandparent->_right;//uncle存在且是红色if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}//uncle不存在或者存在为黑色else{//判断cur是parent的左子树还是右子树://右单旋if (cur == parent->_left){RotateR(grandparent);grandparent->_col = RED;parent->_col = BLACK;}else{RotateL(parent);RotateR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}else{Node* uncle = grandparent->_left;//uncle存在且是红色if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}//uncle不存在或者存在为黑色else{//判断cur是parent的左子树还是右子树://右单旋if (cur == parent->_right){RotateL(grandparent);grandparent->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}_root->_col = BLACK;return true;}//大小int size(){return _size(_root);}//树的高度int height(){return _height(_root);}
private:int _height(Node* root){if (root == nullptr)return 0;int leftheight = _height(root->_left);int rightheight = _height(root->_right);return max(leftheight, rightheight) + 1;}int _size(Node* root){if (root == nullptr)return 0;int left = _size(root->_left);int right = _size(root->_right);return left + right + 1;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left)parentParent->_left = subR;else parentParent->_right = subR;subR->_parent = parentParent;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent)parentParent->_left = subL;else parentParent->_right = subL;subL->_parent = parentParent;}}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << ':' << root->_kv.second << endl;_InOrder(root->_right);}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newnode = new Node(root->_kv);newnode->_left = Copy(root->_left);newnode->_right = Copy(root->_right);return newnode;}Node* _root = nullptr;
};
总结
红黑树作为一种自平衡二叉搜索树,通过严格的颜色和旋转规则,保证了在最坏情况下仍能提供高效的查找、插入和删除操作。其特有的平衡机制使得红黑树在实际应用中被广泛采用,尤其在需要频繁增删节点的数据结构中表现出色。掌握红黑树的原理和操作,不仅有助于理解复杂数据结构的实现,也为开发高效算法奠定了坚实基础。希望通过本篇博客,大家能够对红黑树有更加深入的认识,并在实际编程中灵活应用。
相关文章:
【C++进阶】红黑树
目录 什么是红黑树?红黑树红黑树的性质 定义红黑树红黑树的操作insertinorderfindheightsize构造函数析构函数赋值拷贝判断红黑树 全部代码总结 什么是红黑树? 红黑树 红黑树(Red-Black Tree)是一种自平衡的二叉搜索树ÿ…...
linux使用ssh连接一直弹出密码框问题
1.查看ssh服务的状态 输入以下命令: sudo service sshd status 小编已经安装了。 如果出现 Loaded: error (Reason: No such file or directory) 提示的话,说名没有安装ssh服务,按照第二步:安装ssh服务。 如果出现 Active: in…...
Python 3 数据结构
Python 3 数据结构 引言 Python 是一种高级编程语言,因其简洁明了的语法和强大的功能而广受欢迎。在 Python 中,数据结构是组织和存储数据的方式,对于编写高效和可维护的代码至关重要。本文将深入探讨 Python 3 中的主要数据结构࿰…...
【开源社区】Elasticsearch(ES)中空值字段 null_value 及通过exists查找非空文档
文章目录 0、声明1、问题描述2、问题剖析2.1 NULL或者空值类型有哪些2.2 案例讲解:尝试检索值为 null 的字段2.3 解决思路 3、使用 null_value 的诸多坑(避免生产事故)3.1 null_value 替换的是索引,并不会直接替换源数据3.2 不支持…...
JavaDS —— 位图(BitSet)与 布隆过滤器
位图 引入问题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。 首先要注意 40 亿个数据如果使用 整型(int) 来存放的话,就是要 40 亿个整型,一个整型有…...
如何确保场外个股期权交易的安全?
如何确保场外个股期权交易的安全?投资者可以采取以下措施,以提高交易的安全性和减少风险: 增强知识储备:深入学习期权的基础知识,包括不同类型的期权、它们的权利和义务、定价方式以及风险特性,从而提升自…...
第2章:LabVIEW FPGA未来发展方向《LabVIEW ZYNQ FPGA宝典》
2.1:NI的LabVIEW FPGA未来战略部署 在展望NI公司的LabVIEW FPGA技术未来发展趋势之前,让我们先来回顾一下LabVIEW与FPGA的技术发展历程,如图2-1所示。可以看出,NI公司的LabVIEW FPGA软件一方面是跟随Xilinx最新的FPGA硬件可持续发…...
苹果电脑维护工具:CleanMyMac X让你的Mac焕发新生!
在我们的数字生活中,苹果电脑(Mac)已成为不可或缺的一部分,无论是为工作披星戴月,还是为娱乐畅游云端。但是,就像任何长时间运行的机器一样,Mac也可能会因为积累的文件和不必要的数据而开始变慢…...
MySQL2 DML数据操纵语言和SQL约束
DML和SQL约束 SQL-DML1.添加数据2.修改数据3.删除 TRUNCATE和DELETE的区别:SQL-约束Primary Key创建主键约束单列主键联合主键**验证主键约束**删除主键约束设置主键自增AUTO_INCREMENTdelete和truncate删除后,主键的自增 SQL-唯一约束UNIQUE创建唯一约束…...
Ubuntu 20.04 中安装 Nginx (通过传包编译的方式)、开启关闭防火墙、开放端口号
文章目录 前言一、安装包下载二、上传服务器并解压缩三、依赖配置安装四、生成编译脚本五、编译六、查看是否编译完成七、开始安装八、查看是否安装成功九、设置为开机自启动 前言 参考大佬文章并在基础上做了点修改,发篇文章记录下 防止下次遇到。 参考文章&#…...
解决no main manifest attribute错误
文章目录 0. 背景1. java程序如何运行2. jar是什么3. java -jar test-1.0-SNAPSHOT.jar:4. 添加执行入口 0. 背景 在开发Spring boot项目的时候,有时候会需要使用java -jar test-1.0-SNAPSHOT.jar指令来运行开发的java应用,但是很不幸&#…...
002 | 常见的金融量化指标计算
金融量化指标 在金融量化分析中,常用的指标可以帮助我们判断市场走势、评估风险和收益,以及构建交易策略。以下是一些常见的金融量化指标及其计算方法的详细教程,包括公式与Python代码实现。 1. 移动平均线(Moving Average, MA&…...
Web Vitals:提升用户体验的关键指标
Web Vitals 是 Google 提出的一套核心网页性能指标,旨在帮助开发者理解和优化网站的用户体验。这些指标分为核心 Web Vitals 和附加 Web Vitals,涵盖了加载性能、交互性和视觉稳定性三个方面。以下是详细的介绍和如何使用 Web Vitals 来优化你的网站。 …...
c#中的约束、TimeSpan、defult、operator
c#中的约束 在C#中,约束(Constraints)用于限制泛型类型参数的类型,以确保泛型类型或方法在编译时能够满足特定的要求。约束允许开发者指定泛型类型参数必须满足的条件,比如实现特定的接口或继承自特定的类。以下是一些…...
挖矿木马攻破了服务器
最近被国外的挖矿木马攻破了服务器 根据非法登录,用 #last指令查看登录ip 首先删掉登录主机 #kill -9 pts/0 第二步 #top 看看什么占用cpu高 第三步杀死狂刷CPU的服务 过一分钟后,服务又开始狂刷cpu。 第四步根据pid查到服务地址 #systemctl status…...
从容应对技术面试:策略、技巧与成功案例
欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: 工💗重💗hao💗:野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、应用领域等内容。 ⭐…...
Spring Boot 整合 RestTemplate:详解与实战
Spring Boot 整合 RestTemplate:详解与实战指南 一、引言二、依赖添加Maven 示例:Gradle 示例: 三、创建 RestTemplate 实例四、使用 RestTemplate 发起请求五、处理响应六、高级用法1. 自定义 RestTemplate 实例2. 文件上传、下载以及常见的…...
【利用模板模式和责任链模式实现数据校验】
利用模板模式和责任链模式实现数据校验 一、业务背景二、模板模式和责任链模式代码实现1、数据校验抽象处理器ValidateHandler2、数据校验责任链工具类ValidateChainUtil3、网元调整数据校验抽象类AbstractNodeCheckHandler4、依次定义3个责任链handler,通过Order注…...
学习笔记第十九天
1.标准I/O的基本概念 标准输入(stdin):默认是指键盘输入。 标准输出(stdout):默认是指显示器输出。 标准错误(stderr):用于输出错误信息,也是指向显示器&…...
设计模式 - 单例模式
💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 文章目录 引言一、单例模…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...
