【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):用于输出错误信息,也是指向显示器&…...
设计模式 - 单例模式
💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 文章目录 引言一、单例模…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
