数据结构——红黑树
文章目录
- 一.红黑树的定义
- 二.红黑树的插入
- 1.红黑树节点的定义
- 2.红黑树的插入操作
- 3.总结:
- 三.红黑树与AVL树的比较
- 四.检验手写的红黑树
- 五.源码
一.红黑树的定义
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是
Red
或Black
。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树的性质:
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
解读:
性质三:保证树中没有连续的红色节点
性质四:每条路径上黑色节点的数目相同
满足以上性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍
其中,其极限最短:全黑。极限最长:一黑一红……
可以发现,最坏情况的时间复杂度和AVL
树一样,都是O(logN)
,但是红黑树这种近似平衡的结构减少了大量旋转,综合性能优于AVL
树。
二.红黑树的插入
1.红黑树节点的定义
enum Colour
{Red,Black
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode():_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv){}
};
2.红黑树的插入操作
插入步骤:
-
按照二叉搜索的树规则插入新节点
-
检测新节点插入后,红黑树的性质是否造到破坏
-
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
约定:cur
为当前节点,p
为父节点,g
为祖父节点,u
为叔叔节点-
情况一:
cur
为红,p
为红,g
为黑,u
存在且为红
解决方式:将p,u
改为黑,g
改为红,然后把g
当成cur
,继续向上调整 -
情况二:
cur
为红,p
为红,g
为黑,u
不存在/u
存在且为黑
解决方式:
p
为g
的左孩子,cur
为p
的左孩子,则进行右单旋转;相反,p
为g
的右孩子,cur
为p
的右孩子,则进行左单旋转。p、g
变色–p
变黑,g
变红- 情况三:
cur
为红,p
为红,g
为黑,u
不存在/u
存在且为黑
解决方式:p
为g
的左孩子,cur
为p
的右孩子,则针对p
做左单旋转;相反,p
为g
的右孩子,cur
为p
的左孩子,则针对p
做右单旋转
-
3.总结:
插入新节点时,父节点为红,看叔叔的颜色。
- 叔叔存在且为红,变色,向上调整(可能变为三种情况中的任意一种)
- 叔叔不存在/存在且为黑,直线。单旋+变色
- 叔叔不存在/存在且为黑,折线,两次单旋+变色
三.红黑树与AVL树的比较
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N)
红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
四.检验手写的红黑树
//检查有没有连续红节点
bool Check(Node* root,int blackNum,const int ref)
{if (root == nullptr){if (blackNum != ref){cout << "路径上黑节点数量不一致" << endl;return false;}return true;}if (root->_col == BLACK){++blackNum;}if (root->_col == RED && root->_parent->_col == RED){cout << "违反规则,父子均为红" << endl;return false;}return Check(root->_left, blackNum,ref) && Check(root->_right, blackNum, ref);
}
//统计某条路径的黑色节点数量,然后计算所有路径的黑色节点数量与该路径比对
bool _IsBalance()
{if (_root == nullptr)return true;if (_root->_col != BLACK){return false;}int ref = 0;Node* left = _root;while (left != nullptr){if (left->_col == BLACK){++ref;}left = left->_left;}return Check(_root,0,ref);
}
五.源码
namespace dianxia
{enum Colour{Red,Black};template<class K,class V>struct RBTreeNode{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col; //节点颜色RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv){}};template<class K,class V>class RBTree{typedef RBTreeNode<K,V> Node;public:bool Insert(const pair<K, V>& kv){//直接插入根节点if (_root == nullptr){_root = new Node(kv);_root->_col = Black;return true;}Node* parent = nullptr;Node* cur = _root;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;//检查是否满足红黑树的要求while (parent && parent->_col == Red){//p为红则gp一定存在且为黑Node* grandparent = parent->_parent;assert(grandparent);assert(grandparent->_col==Black);if (parent == grandparent->_left){Node* uncle = grandparent->_right;//1.u存在且为红,变色并继续向上更新if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Black;grandparent->_col = Red;cur = grandparent;parent = cur->_parent;}//2.u不存在或u为黑 变色加旋转else{if (cur == parent->_left){// g// p u// c//右旋RotateR(grandparent);parent->_col = Black;grandparent->_col = Red;}else{// g// p u// c//先左旋再右旋RotateL(parent);RotateR(grandparent);cur->_col = Black;grandparent->_col = Red;}break;}}else //grandparent->_right==parent{Node* uncle = grandparent->_left;//1.u存在且为红,变色并继续向上更新if (uncle && uncle->_col == Red){parent->_col = uncle->_col = Black;grandparent->_col = Red;cur = grandparent;parent = cur->_parent;}//2.u不存在或u为黑 变色加旋转else{if (cur == parent->_right){// g// u p// c//左RotateL(grandparent);parent->_col = Black;grandparent->_col = Red;}else{// g// u p// c//先右旋再左旋RotateR(parent);RotateL(grandparent);cur->_col = Black;grandparent->_col = Red;}break;}}}_root->_col = Black;return true;}~RBTree(){_Destroy(_root);_root = nullptr;}void Inorder(){_InOrder(_root);}//检查红黑树:1.检查以根节点为根的每条路径黑色节点的数目是否都相等// 2.检查某条路径上是否有连续的红色节点bool Isbalance(){if (_root && _root->_col == Red){cout << "根节点是红色的" << endl;return false;}int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == Black)++benchmark;cur = cur->_left;}return _Check(_root, 0, benchmark);}int Height(){return _Height(_root);}private:void _Destroy(Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}int _Height(Node* root){if (root == nullptr)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}bool _Check(Node* root, int blacknum, int benchmark){if (root == nullptr){if (benchmark != blacknum){cout << "某条路径黑色节点的数量不相等" << endl;return false;}return true;}if (root->_col == Black)++blacknum;if (root->_col == Red && root->_parent && root->_parent->_col == Red){cout << "某条路径存在连续的红色节点" << endl;return false;}return _Check(root->_left, blacknum, benchmark)&& _Check(root->_right, blacknum, benchmark);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppnode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}private:Node* _root = nullptr;};
}
本文到此结束,码文不易,还请多多支持!!
相关文章:

数据结构——红黑树
文章目录 一.红黑树的定义二.红黑树的插入1.红黑树节点的定义2.红黑树的插入操作3.总结: 三.红黑树与AVL树的比较四.检验手写的红黑树五.源码 一.红黑树的定义 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色ÿ…...

【C++】数据结构与算法:常用排序算法
😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍常用排序算法。 学其所用,用其所学。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更新不迷路…...
【C++】Bullet3代码存档
之前试了一下Bullet3物理引擎,但在linux上编译失败,于是放弃了。令我不满的还有另外一个原因,下载的发行包竟然有500M。C的Bullet3代码根本用不了,大部分教程实际都是用的老版本。而且此项目还整了python版本,各种蹭人…...

弘扬“两弹一星”精神,勇攀科学技术高峰——道本科技商业大学党日活动圆满落幕
2023年8月2日,道本科技与商业大学携手举办了一场主题为“弘扬‘两弹一星’精神,勇攀科学技术高峰”的党日活动。本次活动旨在了解党领导下的中国核工业发展历程,传承和弘扬“两弹一星”精神,同时展示道本科技创新产品,…...
Java中创建对象的几种方式
背景 面试的时候有些面试官喜欢问这些, 这里简单记录一下. 常见方式 方式1: new XXXX(); 使用new关键字:这是最常见的创建对象的方式,使用new关键字后面跟上类名和参数列表(如果有),可以调用类的构造方法来创建对象…...

Python(三)
诚信像一面镜子,一旦打破,你的人格就会出现裂痕。 存在短路的情景 谢谢观看 Python(三)...

android 如何分析应用的内存(十五)——Visual Studio Code 调试Android应用
android 如何分析应用的内存(十五)——Visual Studio Code 调试Android 应用 在上一篇文章介绍了jdb调试java应用 接下来介绍用UI界面调试java应用,达到同jdb一样的效果。 同样的UI界面有很多选择,如Eclipse,Android …...

宁波银行最新内推码 MK4913
宁波银行最新内推码 MK4913 内推码: MK4913 内推二维码 : 网申路径: 网页端:登录宁波银行招聘官网: https://zhaopin.nbcb.com.cn 选择【校园招聘】-【招聘岗位】手机端:关注【宁波银行招聘】公众号&a…...

postgresql|数据库|MySQL数据库向postgresql数据库迁移的工具pgloader的部署和初步使用
前言: MySQL数据库和postgresql数据库之间的差异并不多,这里的差异指的是对SQL语言的支持两者并不大,但底层的东西差异是非常多的,例如,MySQL的innodb引擎概念,数据库用户管理,这些和postgresq…...

【Python从小白到高手】---函数基础
个人主页:平行线也会相交 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【Python小白从入门到精通】🎈 本专栏旨在分享学习Python的一点学习心得,欢迎大家在评论区讨论💌 目录…...

postman----传参格式(json格式、表单格式)
本文主要讲解postman使用post请求方法的2中传参方式:json格式、表单格式 首先了解下,postman进行接口测试,必须条件是: ♥请求地址 ♥请求协议 ♥请求方式 ♥请求头 ♥参数 json格式 先看一下接口文档,根据接口文档&…...

Uni-Dock:GPU 分子对接使用教程
github文件下载: git clone https://github.com/dptech-corp/Uni-Dock.git cd Uni-Dock/example/screening_test wget https://github.com/dptech-corp/Uni-Dock/releases/download/1.0.0/unidock 将此文件加入到全局变量中 chmod x unidock sudo mv unidock /…...

【Python】数据分析+数据挖掘——掌握Python和Pandas中的单元格替换操作
1. 前言 数据处理和清洗是数据分析和机器学习中至关重要的步骤。在数据处理过程中,我们经常需要对数据集进行清洗和转换,其中单元格替换是一个常用的技术。Python作为一种功能强大且灵活的编程语言,为数据处理提供了丰富的工具和库。Pandas库…...

Godot 4 源码分析 - 增加格式化字符串功能
Godot 4的主要字符串类型为String,已经设计得比较完善了,但有一个问题,格式化这块没怎么考虑。 String中有一个format函数,但这个函数只有两个参数,这咋用? String String::format(const Variant &va…...

C#中XML文档与Treeview控件操作的数据同步
在前文《C#使用XML和Treeview结合实现复杂数据采集功能》中,使用Treeview展示了XML的数据,问题是如果在Treeview上进行了操作,怎样同步更改XML数据的内容呢? 这个问题看似简单,实现起来有一点小麻烦。 要实现的操作功能…...

【Java Web基础】mvn命令、Maven的安装与配置
本文极大程度上来自Maven安装(超详解),但是担心安的过程中遇到什么不一样的问题,顺便加深印象,所以还是打算自己弄一篇。 目录 第一步:Download Maven第二步:解压与安装2.1 解压2.2 安装 第一步:Download …...

加强Web应用程序安全:防止SQL注入
数据库在Web应用程序中存储和组织数据时起着至关重要的作用,它是存储用户信息、内容和其他应用程序数据的中央存储库。而数据库实现了高效的数据检索、操作和管理,使Web应用程序能够向用户提供动态和个性化的内容。然而,数据库和网络应用程序…...

【云原生】k8s中Contrainer 生命周期回调/策略/指针学习
个人主页:征服bug-CSDN博客 kubernetes专栏:kubernetes_征服bug的博客-CSDN博客 目录 1 容器生命周期 2 容器生命周期回调/事件/钩子 3 容器重启策略 4 自定义容器启动命令 5 容器探针 1 容器生命周期 Kubernetes 会跟踪 Pod 中每个容器的状态&am…...

electron+vue3全家桶+vite项目搭建【25】使用electron-updater自动更新应用
文章目录 引入实现效果实现步骤引入依赖配置electron-buidler文件封装版本升级工具类主进程调用版本更新校验渲染进程封装方法调用 测试版本更新 引入 demo项目地址 electron-updater官网 我们不可能每次发布新的版本都让用户去手动下载安装最新的包,而是应用可以…...

SQL 表别名 和 列别名
列表名 列表名之后 order by 可以用别名 也可以用原名, where 中不能用别名的 SQL语句执行顺序: from–>where–>group by -->having — >select --> order 第一步:from语句,选择要操作的表。 第二步࿱…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...