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.mzph.cn/news/717571.shtml

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

相关文章

【Mysql】Navicat数据库勿删了mysql.infoschema@localhost,导致打不开数据库,如何修改

运行报错如下&#xff1a; 1449 . The user specified as a definer (mysql.infoschemaocalhost) does not exist该方法不需要重启mysql&#xff0c;或者重装&#xff1b;仅需要恢复删除的mysql.infoschemalocalhost用户 一、登录建立用户 mysql -uroot -pxxxxxx密码二、建立…

【网上商城系统的设计与开发】

目录 1.实训概况 1 1.1 实训题目 1 1.2实训时间 1 1.3实训目的 1 1.4 实训环境 1 1.5 实训内容 2 1.6 进度安排 3 2.需求分析 5 2.1 功能需求分析 5 2.1.1用户需求分析 5 2.2.2网站前台需求 5 2.2.3网站后台需求 6 2.2 可行性分析 7 2.2.1社会可行性 7 2.2.2技术可行性 8 3.系统…

Sora学习(一):Sora技术路径整体认知

前文&#xff1a;最近跟着DataWhale组队学习这一期“Sora原理与技术实战”&#xff0c;本篇博客主要是基于DataWhale成员、厦门大学平潭研究院杨知铮研究员分享的Sora技术原理详解课件内容以及参考网上一些博客资料整理而来&#xff08;详见文末参考文献&#xff09;&#xff0…

【谈一谈】并发编程_锁的分类

【谈一谈】并发编程_锁的分类 Hello!~大家好!~每天进步一点点,日复一日,我们终将问剑顶峰 这里主要是介绍下我们常用的锁可以分为几类,目的是整体框架作用~方便后续的并发文章 说白了,这篇就是开头哈~ 本文总纲: 一.可重入锁和不可重入锁 我们开发中一般用到的都是可重入锁比如…

Photoshop 2023:重塑创意,引领数字艺术新纪元

在数字艺术的浩瀚星空中&#xff0c;Adobe Photoshop 2023&#xff08;简称PS 2023&#xff09;如同一颗璀璨的新星&#xff0c;为Mac和Windows用户带来了前所未有的创意体验。这款强大的图像处理软件不仅继承了前作的精髓&#xff0c;更在细节上进行了诸多创新&#xff0c;让每…

运行Python文件时出现‘utf-8’code can‘t decode byte 如何解决?(如图)

如图 亦或者出现“SyntaxError: Non-UTF-8 code starting with \xbb ” 出现这种问题往往是编码格式导致的&#xff0c;我们可以在py文件中的第一行加入以下代码&#xff1a; # codingutf-8或者 # codinggdk优先使用gbk编码 解释一下常用的两种编码格式&#xff1a; utf-…

朱维群将出席用碳不排碳碳中和顶层科技路线设计开发

演讲嘉宾&#xff1a;朱维群 演讲题目&#xff1a;“用碳不排碳”碳中和顶层科技路线设计开发 简介 姓名&#xff1a;朱维群 性别&#xff1a;男 出生日期&#xff1a;1961-09-09 职称&#xff1a;教授 1998年毕业于大连理工大学精细化工国家重点实验室精细化工专业&…

什么是B+树,和B树有什么不同?

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

Spring Initializer环境问题

1.基于jdk8与本地 环境准备 1)下载jdk8并安装 2&#xff09;下载maven 3.6.3并解压放入D盘maven目录下&#xff0c;去掉外层 设置阿里源 打开settings.xml,在mirrors标签之内增加&#xff0c;注意粘贴后</id>中的/有可能被删掉&#xff0c;要自己补上 <mirror>&l…

健身房预约小程序制作详细步骤解析

如果你是一位健身爱好者&#xff0c;或者是一位健身教练&#xff0c;你一定知道预约健身的痛苦。传统的预约方式不仅麻烦&#xff0c;而且效率低下。但是&#xff0c;现在&#xff0c;我们可以使用一种神仙工具——乔拓云网&#xff0c;来搭建一个属于自己的健身预约小程序&…

【VTKExamples::PolyData】第四十三期 PolyDataPointSampler

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享VTK样例PolyDataPointSampler,并解析接口vtkPolyDataPointSampler,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO …

如何使用 CrewAI 构建协作型 AI Agents

一、前言 AI Agents 的开发是当前软件创新领域的热点。随着大语言模型 (LLM) 的不断进步&#xff0c;预计 AI 智能体与现有软件系统的融合将出现爆发式增长。借助 AI 智能体&#xff0c;我们可以通过一些简单的语音或手势命令&#xff0c;就能完成以往需要手动操作应用程序才能…

运维的利器–监控–zabbix–grafana

运维的利器–监控–zabbix–grafana 一、介绍 Grafana 是一个跨平台的开源的度量分析和可视化工具 , 可以通过将采集的数据查询然后可视化的展示 。zabbix可以作为数据源&#xff0c;为grafana提供数据&#xff0c;然后grafana将数据以图表或者其他形式展示出来。zabbix和gra…

基于YOLOv的目标追踪与无人机前端查看系统开发

一、背景与简介 随着无人机技术的快速发展&#xff0c;目标追踪成为无人机应用中的重要功能之一。YOLOv作为一种高效的目标检测算法&#xff0c;同样适用于目标追踪任务。通过集成YOLOv模型&#xff0c;我们可以构建一个无人机前端查看系统&#xff0c;实现实时目标追踪和可视化…

零基础学编程,中文编程工具之进度标尺构件的编程用法

零基础学编程&#xff0c;中文编程工具之进度标尺构件的编程用法 一、前言 今天给大家分享的中文编程开发语言工具 进度条构件的用法。 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 编程工具及实例源码文件下载可以点击最下方官网卡片——软件下载——…

机器人持续学习基准LIBERO系列9——数据集轨迹查看

0.前置 机器人持续学习基准LIBERO系列1——基本介绍与安装测试机器人持续学习基准LIBERO系列2——路径与基准基本信息机器人持续学习基准LIBERO系列3——相机画面可视化及单步移动更新机器人持续学习基准LIBERO系列4——robosuite最基本demo机器人持续学习基准LIBERO系列5——…

Python AI 实现绘画功能(附带源码)

本文我们将为大家介绍如何基于一些开源的库来搭建一套自己的 AI 作图工具。 需要使用的开源库为 Stable Diffusion web UI&#xff0c;它是基于 Gradio 库的 Stable Diffusion 浏览器界面 Stable Diffusion web UI GitHub 地址&#xff1a;GitHub - AUTOMATIC1111/stable-dif…

快速解决maven依赖冲突

我们在开发过程中经常出现maven依赖冲突&#xff0c;或者maven版本不匹配的情况&#xff0c;我们可以使用阿里云原生脚手架来做maven管理&#xff0c;添加需要的组件&#xff0c;然后点击获取代码&#xff0c;就可以获得对应的依赖文件。

【重要公告】对BSV警报系统AS的释义

​​发表时间&#xff1a;2024年2月15日 由BSV区块链协会开发并管理的BSV警报系统&#xff08;Alert System&#xff0c;以下简称“AS”&#xff09;是BSV网络的重要组件。它是一个复杂的系统&#xff0c;主要职能是在BSV区块链网络内发布信息。这些信息通常与网络访问规则NAR相…

C++基于多设计模式下的同步异步日志系统day4

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C基于多设计模式下的同步&异步日志系统 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 只要内容主要实现了同步日志消息…