【C++杂货铺】红黑树

目录
🌈前言🌈
📁 红黑树的概念
📁 红黑树的性质
📁 红黑树节点的定义
📁 红黑树的插入操作
📁 红黑树和AVL树的比较
📁 全代码展示
📁 总结
🌈前言🌈
欢迎大家观看本期【C++杂货铺】,本期内容将讲解二叉搜索树的进阶——红黑树。红黑树是一种二叉搜索树,通过控制每个节点的颜色,来调整整棵树的高度。
红黑树是set和map实现的底层实现,在下一期内容,我们将讲解STL中set和map的模拟实现。如果你对二叉搜索树还不是很了解,可以快速阅览下面这篇文章;
【C++杂货铺】二叉搜索树-CSDN博客
📁 红黑树的概念
红黑树,是一种二叉搜索树,在每个节点增加一个存储位表示节点的颜色,可以是Red或Black。通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而接近平衡的。
📁 红黑树的性质
1. 每个节点不是红色就是黑色;
2. 根节点是黑色的;
3. 如果一个节点是红色的,那么它的两个孩子节点是黑色的;
4. 对于每个节点,从该节点到其他所有后代叶节点的简单路径上,均包含相同数目的黑色节点个数;
5. 每个叶子结点都是黑色的(此后的叶子结点指的是空节点)
📁 红黑树节点的定义
// 节点的颜色
enum Color{RED, BLACK};
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{RBTreeNode(const ValueType& data = ValueType(),Color color = RED): _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _color(color){}RBTreeNode<ValueType>* _pLeft; // 节点的左孩子RBTreeNode<ValueType>* _pRight; // 节点的右孩子RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给
出该字段)ValueType _data; // 节点的值域Color _color; // 节点的颜色
};
📁 红黑树的插入操作
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
1. 按照二叉搜索树规则插入新节点;
新插入节点的默认颜色是红色。因为红色容易控制规则,如果默认插入黑色,需要保证从当前节点到叶子节点具有相同的黑色节点个数,不易控制。
即先保证性质4不被违反,再来判断性质3是否被违反,如果违反就进行调整。
2. 检测新节点插入后,红黑树的性质是否遭到破坏。
如果双亲节点的颜色是黑色,没有违反规则,则不需要调整;当新插入节点的双亲节点颜色为红色时,就违反了性质3,即不能有连续在一起的红色节点,此时需要进行调整,可分为两种情况:
约定:cur为当前节点,p为父亲节点,g为祖父节点,u为叔叔节点
● 情况1:cur为红,p为红,g为黑,u存在且为红

● 情况2:cur为红,p为红,g为黑,u存在且为黑 或者 u不存在

当如子树如下图所示时,需要对红黑树进行双旋,先以p为根进行左旋,再以g为根进行右旋。下图是p在g的左子树的情况,考虑一下p在g的右子树,且cur为p的左子树的情况。

📁 红黑树和AVL树的比较
红黑树和AVL数都是搞笑的平衡二叉树,增删查改的时间复杂度都是O(log N),红黑树不追求绝对平衡,其只需要保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL数更优,而且红黑树的实现比较简单,所以实际应用中红黑树更多。
map和set的底层就是红黑树。
📁 全代码展示
template <class T>
struct RBTreeNode
{typedef RBTreeNode<T> Node;RBTreeNode(const T& val = T()):_left(nullptr), _right(nullptr), _parent(nullptr), _val(val), _color(RED){}Node* _left;Node* _right;Node* _parent;T _val;Color _color;
};template<class T>
class RBTree
{typedef RBTreeNode<T> Node;
public:bool Insert(const T& val){if (_root == nullptr){_root = new Node(val);_root->_color = Black;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_val > val){parent = cur;cur = cur->_left;}else if (cur->_val < val){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(val);if (parent->_val < val){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//调整颜色,不能出现连续的红色while (parent && parent->_color == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){//叔叔在右边//1. 叔叔存在,且为红色Node* uncle = grandfather->_right;if (uncle && uncle->_color == RED){grandfather->_color = RED;uncle->_color = parent->_color = Black;cur = grandfather;parent = cur->_parent;}//2. 叔叔存在,且为黑色 || 叔叔不存在else{/*gp uc */if (cur == parent->_left){RotateR(grandfather);parent->_color = Black;grandfather->_color = RED;}/*gp uc*/else{RotateL(parent);RotateR(grandfather);cur->_color = Black;grandfather->_color = RED;}break;}}else{//叔叔在左边//1. 叔叔存在,且为红色Node* uncle = grandfather->_left;if (uncle && uncle->_color == RED){grandfather->_color = RED;parent->_color = uncle->_color = Black;cur = grandfather;parent = cur->_parent;}//2. 叔叔存在,且为黑色 || 叔叔不存在else{/*gu pc*/if (cur == parent->_right){RotateL(grandfather);parent->_color = Black;grandfather->_color = RED;}/*gu pc */else{RotateR(parent);RotateL(grandfather);cur->_color = Black;grandfather->_color = RED;}break;}}}_root->_color = Black;return true;}//遍历
void InOrder()
{_InOrder(_root);cout << endl;
}bool isBalance()
{if (_root == nullptr){return true;}int BlackNum = 0;Node* cur = _root;while (cur){if (cur->_color == Black)BlackNum++;cur = cur->_right;}return _isBalance(_root,0,BlackNum);
}protected:bool _isBalance(Node* root,int count , const int& BlackNum){if(root == nullptr){if (count != BlackNum)return false;return true;}if (root->_color == RED && root->_parent->_color == RED){cout << "red" << endl;return false;}if (root->_color == Black)count++;return _isBalance(root->_left,count,BlackNum)&& _isBalance(root->_right,count,BlackNum);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_val << endl;_InOrder(root->_right);}//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* pparent = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (parent == pparent->_right){pparent->_right = subR;}else{pparent->_left = subR;}subR->_parent = pparent;}}//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* pparent = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (parent == pparent->_right){pparent->_right = subL;}else{pparent->_left = subL;}subL->_parent = pparent;}}
private:Node* _root = nullptr;
};
📁 总结
以上就是本期【C++杂货铺】的主要内容了,讲解了红黑树如果优化二叉搜索树,红黑树的概念,红黑树实现插入,以及红黑树的高度平衡调整,此外模拟实现了红黑树。
如果感觉本期内容对你有帮助,欢迎点赞,收藏,关注Thanks♪(・ω・)ノ

相关文章:
【C++杂货铺】红黑树
目录 🌈前言🌈 📁 红黑树的概念 📁 红黑树的性质 📁 红黑树节点的定义 📁 红黑树的插入操作 📁 红黑树和AVL树的比较 📁 全代码展示 📁 总结 🌈前言…...
css--控制滚动条的显示位置
各种学习后的知识点整理归纳,非原创! ① direction属性 滚动条在左侧显示② transform:scaleY() 滚动条在上侧显示 正常的滚动条会在内容超出规定的范围后在区域右侧和下侧显示在有些不正常的需求下会希望滚动条在上侧和左侧显示自己没有想到好的解决方案…...
华为设备display查看命令
display version //查看版本信息 display current-configuration //查看配置详情 display this //查看当前视图有效配置 display ip routing-table //查看路由表 display ip routing-table 192.168.3.1 //查看去往3.1的路由 display ip interface brief //查看接口下ip信息 dis…...
自动攻丝机进出料激光检测 进料出料失败报警循环手动及关闭报警退出无限循环
/**************进料检测********************/ /***缺料无限次循环 手动退出 超时报警*******/ void check_Pon() // { zstatus0; //报警计数器归零 Signauto1; …...
2024年去除视频水印的5种方法
如果你从事电影剪辑或者视频编辑工作,你经常需要从优酷、抖音、TikTok下载各种视频片段……。 通常这些视频带有水印和字幕。一些免费软件如CapCut、canva、Filmora也会给你制作的视频打上水印,这些水印嵌入在视频内部。 2024年去除视频水印的5种方法 …...
怎么用电脑接收手机文件 用备忘录传输更舒服
在这个数字化时代,手机已经成为我们随身携带的“百宝箱”,里面装满了各种重要的文件、资料和信息。然而,有时我们需要在电脑上处理这些文件,比如编辑文档、制作PPT或是查看照片。那么,如何在电脑与手机之间实现文件的顺…...
微信小程序、uniapp密码小眼睛
直接上代码喔喔喔喔喔喔喔喔~~ <input name"username" password"{{passwordHideShow}}" placeholder-style"color:#bdbdbd" type"text"maxlength"20" value"{{passwordNumber}}" bindinput"passwordInput…...
【手势操作-复习前一天的内容-预习今天的内容 Objective-C语言】
一、昨天呢,我们学习的是这个,事件 1.事件这一块儿呢,iOS事件,分为三大类, 1)触摸事件 2)加速计事件 3)远程控制事件 2.这个里边呢,我们主要学习的是这个触摸事件,触摸事件里边,就是Touch,touchesBegan:方法里边,有一个touches参数,它是set类型的, 3.Set,…...
【收录 Hello 算法】第 6 章 哈希表
目录 第 6 章 哈希表 本章内容 第 6 章 哈希表 Abstract 在计算机世界中,哈希表如同一位聪慧的图书管理员。 他知道如何计算索书号,从而可以快速找到目标图书。 本章内容 6.1 哈希表6.2 哈希冲突6.3 哈希算法6.4 小结...
rust类型和变量(二)
基础知识 Rust中的变量基础知识 1.在Rust中,使用Iet关键字来声明变量 2.Rust支持类型推导,但你也可以显式指定变量的类型: Ietx:i325;/显式指定x的类型为i32 3.变量名蛇形命名法(Snake Case),i 而枚举和结构体命名使用帕斯卡命名法(Pasca|Ca…...
linux学习:多媒体开发库SDL+视频、音频、事件子系统+处理yuv视频源
目录 编译和移植 视频子系统 视频子系统产生图像的步骤 api 初始化 SDL 的相关子系统 使用指定的宽、高和色深来创建一个视窗 surface 使用 fmt 指定的格式创建一个像素点编辑 将 dst 上的矩形 dstrect 填充为单色 color编辑 将 src 快速叠加到 dst 上编辑 更新…...
基于门控的循环神经网络:LSTM
之前我们介绍了循环神经网络的原理以及实现。但是循环神经网络有一个问题,也就是长期依赖问题。我们之前的01序列预测案例中可以看到,当序列长度到达10以上之后错误就会增多,说明简单的RNN记忆容量较小,当长度更大时就不怎么适用了…...
Web常见的攻击方式及其防御策略
随着互联网技术的快速发展,Web应用已成为我们日常生活和工作中不可或缺的一部分。然而,Web应用也面临着各种安全威胁和攻击。了解这些常见的攻击方式,并采取有效的防御策略,对于保护Web应用的安全至关重要。 一、常见的Web攻击方…...
关于SQL
数据库简介: 数据库分类 关系型数据库模型: 优点:易于维护,可以实现复杂的查询 缺点:海量数据 读取写入性能差,高并发下数据库的io是瓶颈 是把复杂的数据结构归结为简单的二元关系(即二维表…...
大模型时代下两种few shot高效文本分类方法
介绍近年(2022、2024)大语言模型盛行下的两篇文本分类相关的论文,适用场景为few shot。两种方法分别是setfit和fastfit,都提供了python的包使用方便。 论文1:Efficient Few-Shot Learning Without Prompts 题目:无需提示的高效少…...
Linux0.11 中全局描述符表(GDT)
在Linux内核中,全局描述符表(Global Descriptor Table,简称GDT)是一个关键的数据结构,主要用于管理处理器的内存段和相关的权限与属性。它属于x86架构中的保护模式特性,允许操作系统对内存访问进行更精细的…...
搜维尔科技:数据手套用于外固定虚拟现实模拟 、外固定增强现实模拟
数据手套用于外固定虚拟现实模拟、外固定增强现实模拟 搜维尔科技:数据手套用于外固定虚拟现实模拟、外固定增强现实模拟...
《三》菜单栏_工具栏_状态栏动作与实现
上期我们创建了辣么多的动作,那么这次我们要是开始实现这些动作,撸起袖子来吧: //菜单动作(ACtion)QAction *newAct;//新建QAction *openAct;//打开QAction *saveAct;//保存QAction *saveAsAct;//另存为QAction *prin…...
基于NTP服务器获取网络时间的实现
文章目录 1 NTP1.1 简介1.2 包结构1.3 UNIX 时间戳和NTP时间戳 2 代码实现2.1 实现步骤2.2 完整代码 3 结果 在某些场景下,单片机需要通过网络获取准确的时间进行数据同步,例如日志记录、定时任务等。然而,单片机本身无法直接获得准确的标准时…...
Web APIs(获取元素+操作元素+节点操作)
目录 1.API 和 Web API 2.DOM导读 DOM树 3.获取元素 getElementById获取元素 getElementsByTagName获取元素 H5新增方法获取 获取特殊元素 4.事件基础 执行事件 操作元素 修改表单属性 修改样式属性 使用className修改样式属性 获取属性的值 设置属性的值 移除…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
基于Uniapp的HarmonyOS 5.0体育应用开发攻略
一、技术架构设计 1.混合开发框架选型 (1)使用Uniapp 3.8版本支持ArkTS编译 (2)通过uni-harmony插件调用原生能力 (3)分层架构设计: graph TDA[UI层] -->|Vue语法| B(Uniapp框架)B --&g…...

