C++_红黑树

       

目录

1、红黑树的规则

2、红黑树节点的定义 

3、红黑树插入节点的调整操作

3.1 情况一

3.2 情况二

3.3 情况三 

4、红黑树的实现

结语


前言:

        在C++中,红黑树是二叉搜索树的另一种优化版本,他与AVL树的区别在于保持树的平衡方式不同,AVL树保持平衡的方式是在节点中多存储一个成员来记录平衡因子,红黑树保持平衡的方式也是增加了一个成员,但是该成员的作用是记录节点的两种状态(颜色):红色--黑色。当然只记录颜色并不能保持平衡,红黑树还规定最长路径的节点个数不会超过最短路径的节点个数的两倍,因此红黑树不会因为插入有序数据而演变成“单支树”。

1、红黑树的规则

        红黑树有如下规则:

        1、顾名思义,红黑树的节点只能有黑色和红色两种状态。

        2、根结点默认为黑色。

        3、红色节点的两个孩子只能是黑色节点。

        4、插入的节点默认为红色节点。

        5、每条路径的黑色节点都相同。

        红黑树正确示意图:

         红黑树错误示意图:

2、红黑树节点的定义 

        通过上述对红黑树的简述,可以给出红黑树的节点代码:

#define _CRT_SECURE_NO_WARNINGS 1enum 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;//若是AVL树这里记载的是平衡因子,红黑树是颜色RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED)//默认插入的节点是红色{}
};

        可以发现,红黑树的节点代码几乎和AVL树一模一样,只是控制平衡的条件有区别,仅此而已。 

3、红黑树插入节点的调整操作

        红黑树的插入函数可以分两个步骤:

        1、找到合适的位置插入,即二叉搜索树插入的逻辑(小于根节点的放在左边,大于根节点的放在右边)。

        2、因为插入的节点默认为红色,则插入节点后,查看当前树是否破坏了红黑树的规则,即观察其节点的父母节点是否为红色,如果是则需要进行调整操作(规则3)。

        在分析之前,先确定好节点之间的关系名称(cur表示新插入的节点,parant表示父母节点,uncle表示叔叔节点,gparent表示祖父节点):

3.1 情况一

        当叔叔节点存在且为红,父母节点为红,祖父节点为黑:

        最后可以发现,经过一系列的调整后符号红黑树的规则。

3.2 情况二

        情况二又分两种情况:1、叔叔节点为黑色。2、不存在叔叔节点。

        1、其他条件和情况一相同,但是叔叔节点是黑色的:

        从上图可以发现,情况二多了旋转的步骤,并且在旋转之后将parent变黑,gparent变红,最终结果满足红黑树的规则。

        2、若不存在叔叔节点:

        综上所述,情况二可以总结为:旋转+变色(parent变黑,gparent变红)。 

3.3 情况三 

        情况三即以上情况的插入点不一样,以上情况的插入节点都是插入在“边缘处”,通俗来讲就是左子树插入的节点都是作为左孩子插入的,而右子树插入的节点都是作为右孩子插入的,但是实际中总会出现在右子树中插入的节点是以左孩子的形式插入的(如下图),拿上述情况二的第二种情况举例,若插入的cur在parent的左边,那么以上的处理方法显然不能解决问题,具体操作图如下:

4、红黑树的实现

        红黑树的实现代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;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;//若是AVL树这里记载的是平衡因子,红黑树是颜色RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED)//默认插入的节点是红色{}
};template<class K, class V>
class RBTree//红黑树类
{typedef RBTreeNode<K, V> Node;
public:~RBTree(){_Destroy(_root);_root = nullptr;}
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);if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//当parent为红色则违法规则,需要调整while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent)//父母在祖父的左边{Node* uncle = grandfather->_right;// 情况1:叔叔节点存在且为红,叔叔和父母进行变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2:叔叔不存在/叔叔存在且为黑,旋转+变色{//父母在祖父的左边,而cur在父母的左边,说明是“边缘”情况if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//对应所述的情况三,需要旋转两次,并且cur变成了根结点{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else // 父母在祖父的右边{Node* uncle = grandfather->_left;// 情况1:u存在且为红,变色处理,并继续往上处理if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;// 继续往上调整cur = grandfather;parent = cur->_parent;}else // 情况2:叔叔不存在/叔叔存在且为黑,旋转+变色{//以下逻辑同上思路,只不过逻辑相反if (cur == parent->_right){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;//根结点始终为黑return true;}void InOrder(){_InOrder(_root);}bool IsRedB()//检查红黑树{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 == NULL)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;}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 _InOrder(Node* root)//中序遍历打印{if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}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;
};int main()
{int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };RBTree<int, int> t1;for (auto e : a){t1.Insert(make_pair(e, e));}t1.InOrder();//j检查打印出来的数据是否有序cout << endl << t1.IsRedB() << endl;return 0;
}

         运行结果:

        从上面代码可以看出,检验一棵树是否为红黑树,从以下几个方面进行判断:函数IsRedB的作用是判断根结点是否为黑色,然后随便遍历一条路径,记录该路径下黑色节点的数量(规则5)。 

        接着把记录下来的黑色节点数量传给函数_check,并且让_check函数把所有路径下的黑色节点记录下来,一一的去跟之前记录好的数据进行对比,若有不相等的情况说明该树不是红黑树,另外_check函数内还进行了红色节点是否连续的判断(规则3)。

结语

        以上就是关于红黑树的讲解,红黑树的重点还是在于了解红黑树的调整逻辑,理清叔叔节点和父母节点他们的位置关系,最核心的还是叔叔节点,他的存在与否,是红色还是黑色都影响最终的调整规律。最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/268357.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

django的模板渲染中的【高级定制】:按数据下标id来提取数据

需求&#xff1a; 1&#xff1a;在一个页面中显示一张数据表的数据 2&#xff1a;不能使用遍历的方式 3&#xff1a;页面中的数据允许通过admin后台来进行修改 4&#xff1a;把一张数据表的某些内容渲染到[xxx.html]页面 5&#xff1a;如公司的新商品页面&#xff0c;已有固定的…

波斯猫 6页面 宠物动物 长毛猫 HTML5 带背景音乐 JS图片轮播特效 滚动文字 鼠标经过图片 JS时间代码

波斯猫 6页面 宠物动物 长毛猫 HTML5 带背景音乐 JS图片轮播特效 滚动文字 鼠标经过图片 JS时间代码 注册表单 宠物网页成品 海量学生网页成品 个人博客 人物明星 城市家乡 旅游景点 美食特产 购物电商 公司企业 学校大学 科普教育 宠物动物 鲜花花卉 植物水果 茶叶咖啡 健康生…

react native封装ScrollView,实现(滑到底部)和(滑到顶部+手指继续向下滑)时拉取新数据

里面的tw是在react native中使用tailwind的第三方库 只求读者把样式看个大概&#xff0c;主要还是功能的实现 ScrollView的官方文档如下 https://reactnative.cn/docs/scrollview import tw from twrnc import { View, Text, ScrollView, RefreshControl } from react-native …

Python用类实现抽象和封装

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 路在脚下&#xff0c;勇往直前&#x…

Git——Upload your open store

0.default config ssh-keygen -t rsa #之后一路回车,当前目录.ssh/下产生公私钥 cat ~/.ssh/id_rsa.pub #复制公钥到账号 git config --global user.email account_email git config --global user.name account_name1. 上传一个公开仓库 查看当前分支&#xff1a; git branc…

去中心化时代,品牌如何赢得确定性增长

去中心化时代下&#xff0c;品牌面临众多挑战。在如今复杂的环境下&#xff0c;有很多不确定的因素&#xff0c;流量、资本等等&#xff0c;这些都是品牌发展过程中的不确定因素&#xff0c;越是复杂的环境下&#xff0c;品牌越要保证自己核心优势&#xff0c;找到并放大我们的…

华为配置攻击检测功能示例

配置攻击检测功能示例 组网图形 图1 配置攻击检测功能示例组网图 业务需求组网需求数据规划配置思路配置注意事项操作步骤配置文件 业务需求 企业用户通过WLAN接入网络&#xff0c;以满足移动办公的最基本需求。且在覆盖区域内移动发生漫游时&#xff0c;不影响用户的业务使用。…

AI大预言模型——ChatGPT与AI绘图及论文高效写作

原文链接&#xff1a;AI大预言模型——ChatGPT与AI绘图及论文高效写作 2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网…

【风格迁移】AdaAttN:使用注意力机制和归一化来保持内容结构的同时转移风格特征

AdaAttN&#xff1a;使用注意力机制和归一化来保持内容结构的同时转移风格特征 提出背景AdaAttN 框架自适应注意力归一化&#xff08;AdaAttN&#xff09;损失函数视频风格迁移的扩展 自适应注意力归一化&#xff08;AdaAttN&#xff09;的应用场景 全流程优化基于特征相似度的…

go 命令行框架cobra

go 命令行框架cobra go 拉取依赖包go get github.com/spf13/cobra 认识spf13/cobra-cli. cobra 命令行框架在golang中的地位也算得上是大明星级别。像k8s,docker都有使用这个框架构建自己命令行这块的功能. 最最最简单的开始----使用命令行工具cobra-cli来初始化你的demo c…

03-grafana的下拉列表选项制作-grafana的变量

一、准备环境 为了实现下拉列表筛选的样例&#xff0c;我们监控两个linux节点&#xff1b; 目前&#xff0c;我们已经有了一个节点了&#xff0c;再添加一个&#xff1b; 二、grafana的仪表盘变量 如果想给仪表盘自定义下拉列表&#xff0c;那么&#xff0c;需要设置变量&#…

Flink StreamGraph生成过程

文章目录 概要SteramGraph 核心对象SteramGraph 生成过程 概要 在 Flink 中&#xff0c;StreamGraph 是数据流的逻辑表示&#xff0c;它描述了如何在 Flink 作业中执行数据流转换。StreamGraph 是 Flink 运行时生成执行计划的基础。 使用DataStream API开发的应用程序&#x…

分享经典、现代和前沿软件工程课程

随着信息技术的发展&#xff0c;软件已经深入到人类社会生产和生活的各个方面。软件工程是将工程化的方法运用到软件的开发、运行和维护之中&#xff0c;以达到提高软件质量&#xff0c;降低开发成本的目的。软件工程已经成为当今最活跃、最热门的学科之一。 本次软件工程MOOC课…

SAP PP学习笔记05 - BOM配置(Customize)1 - 修正参数

上次学习了BOM相关的内容。 SAP PP学习笔记04 - BOM1 - BOM创建&#xff0c;用途&#xff0c;形式&#xff0c;默认值&#xff0c;群组BOM等_sap销售bom与生产bom-CSDN博客 SAP PP学习笔记04 - BOM2 -通过Serial来做简单的BOM变式配置&#xff0c;副明细&#xff0c;BOM状态&…

Matlab 最小二乘插值(曲线拟合)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 在多项式插值时,当数据点个数较多时,插值会导致多项式曲线阶数过高,带来不稳定因素。因此我们可以通过固定幂基函数的最高次数 m(m < n),来对我们要拟合的曲线进行降阶。之前的函数形式就可以变为: 二、实现…

Unity绘制六边形体

现在steam上面有很多下棋类/经营类的游戏都是用六边形的地形&#xff0c;比较美观而且实用&#xff0c;去年在版本末期我也自己尝试做了一个绘制六边体的demo&#xff0c;一年没接触unity竟然都要忘光了&#xff0c;赶紧在这边记录一下。 想cv代码可以直接拉到代码章节 功能 …

力扣周赛387

第一题 代码 package Competition.The387Competitioin;public class Demo1 {public static void main(String[] args) {}public int[] resultArray(int[] nums) {int ans[]new int[nums.length];int arr1[]new int[nums.length];int arr2[]new int[nums.length];if(nums.leng…

[AutoSar]BSW_Com09 CAN driver 模块FULL(BASIC)CAN、FIFO选择

目录 关键词平台说明一、FULL CAN 和Basic CAN 关键词 嵌入式、C语言、autosar、OS、BSW 平台说明 项目ValueOSautosar OSautosar厂商vector &#xff0c;芯片厂商TI 英飞凌编程语言C&#xff0c;C编译器HighTec (GCC)autosar版本4.3.1 >>>>>回到总目录<&…

云计算市场,从追求“规模制胜”到走向“用户分化”

文|智能相对论 作者|叶远风 通常来说&#xff0c;价格战放到任何行业&#xff0c;都不是什么好事。 如今&#xff0c;作为曾经的前沿技术创新&#xff0c;云计算行业正在被迫走入价格战的阴霾当中&#xff0c;引发业界担忧。 ECS&#xff08;云服务器&#xff09;最高降36%…

wordpress外贸独立站

WordPress外贸电商主题 简洁实用的wordpress外贸电商主题&#xff0c;适合做外贸跨境的电商公司官网使用。 https://www.jianzhanpress.com/?p5025 华强北面3C数码WordPress外贸模板 电脑周边、3C数码产品行业的官方网站使用&#xff0c;用WordPress外贸模板快速搭建外贸网…