拿捏红黑树(C++)

文章目录

  • 前言
  • 一、红黑树介绍
  • 二、插入操作
  • 三、验证红黑树
  • 四、红黑树与AVL性能比较与应用
  • 五、总体代码
  • 总结


前言

我们之前介绍了一种AVL的高阶数据结构,在本篇文章中,我们将会介绍一种与AVL旗鼓相当的数据结构–红黑树。
我们并且会对它的部分接口进行模拟实现

一、红黑树介绍

AVL是保证左右高度不超过1,实现平衡。
红黑树是在每个节点存储位表示颜色,包括红色和黑色,并且保证最长路径的节点个数不超过最短节点路径的两倍,我们就可以达到一种近似平衡
在这里插入图片描述

性质

🌟每个节点颜色不是红色就是黑色
🌟根节点是黑色的
🌟如果一个节点是红色,那么它的孩子必须是黑色节点(不允许出现连续的红色节点
🌟每条路径都包含相同数量的黑色节点(路径:根节点到空)
🌟空节点设置为黑色,这个节点也称为NIL节点

为什么这几条规则就可以保证最长路径的节点数量不超过最短路径节点数量的两倍呢??

我们从极端场景分析:
最短路径:全黑节点
最长路径:一黑一红

二、插入操作

我们需要一个位置表示颜色,这里我们采用枚举(enum)的方式。

我们插入到节点是插入红色呢还是黑色呢??

我们看一下主要的规则:每条路径都包含相同数量的黑色节点;不允许出现连续的红色节点。

如果我们插入黑色节点,每条路径都会受到影响,我们是很难控制调整的。
如果我们插入红色节点,不允许出现连续的红色节点。我们只是在一条路径上插入,只需要调整这条路径上的节点,保证不出现连续的红色节点就可以。

先把大框架实现。

enum Col
{RED,BLACK
};template<class K,class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Col  _col;//表示颜色pair<K, V>_kv;RBTreeNode(const pair<K, V>kv):_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED)//初始化为红色,_kv(kv){}
};template<class K,class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:private:Node* _root = nullptr;
};

如何插入节点呢??
1.按照二叉搜索树规则找到插入节点
2.进行颜色调整

我们插入的是红色节点,如果父亲节点的颜色也是红色,我们就需要进行调整。
如果父亲节点的颜色是黑色,我们就不需要进行处理,直接退出。

调整:
我们这里的关键是看叔叔
因为我们已经知道我们插入的节点是红色,并且父亲也是红色,爷爷节点必定是黑色。
我们唯一不确定的就是叔叔节点

uncle存在且为红

在这里插入图片描述

我们分析一下具象图

当a/b/c/d/e都为空时,cur就是新插入节点。
在这里插入图片描述

我们需要把p和u变黑,同时把g变黑
在这里插入图片描述

如果这个节点是根节点,我们就把g变黑。
否则cur=g,p=g->p;继续调整。(原来根是黑色,现在变为红色)

当c/d/e是包含一个黑色节点的子树

有四种情况
在这里插入图片描述

我们选择最简单的看一下,插入的位置有四个选择。
在这里插入图片描述

我们把p和u变黑,同时g变红

下图才是我们这种去情况的具象图,这种情况是由之前的情况调整过来的。
在这里插入图片描述

把p和u变黑,同时g变红
如果这个节点是根节点,我们就把g变黑。
否则cur=g,p=g->p;继续调整。(原来根是黑色,现在变为红色)

在这里插入图片描述
我们发现g就是根节点,我们把g变黑

在这里插入图片描述

我们发现现在每条路径上黑节点的数量增加了,由原来两个黑节点变为现在3个黑节点。
黑色节点的数量增加是在根节点增加的,根节点增加就相当于每条路径都增加。

uncle不存在,或者存在且为黑

在这里插入图片描述

如果u不存在,cur就是新增加点,我们通过之前的变色已经完成不了了。
在这里插入图片描述

我们这时就需要进行旋转,旋转方式还是按照AVL树的情况。
这个场景下我们需要右旋

在这里插入图片描述
旋转完之后,进行变色,p变黑,g变红
在这里插入图片描述

如果是下面这种情况,我们就需要进行双旋之后,再进行变色
在这里插入图片描述

首先对p进行左旋
在这里插入图片描述

再对g进行右旋
在这里插入图片描述
最后进行变色,cur变为黑,g变为红
在这里插入图片描述

总结:
🌟我们新插入节点颜色是红色
🌟如果新插入节点的父亲节点是黑色,我们不进行调整,直接退出
🌟如果新插入节点的父亲节点是红色,此时关键看叔叔。
🌟如果叔叔存在且为红,将p和u变黑,g变红。判断g是否为根节点。如果为根节点,g变黑。否则继续调整。
我们不关心左右,p和u是g的左右都不收影响,cur是p的左右也不受影响。
🌟如果uncle不存在或者存在且为黑,调整完之后结束。原来根是黑色,现在根也是黑色,不影响
🌟p为g的左孩子,cur为p的左孩子,对g进行右单旋,p变黑,g变红
🌟p为g的右孩子,cur为p的右孩子,对g进行左单旋,p变黑,g变红
🌟p为g的左孩子,cur为p的右孩子,对p进行左单旋,对g进行右单旋,c变黑,g变红
🌟p为g的右孩子,cur为p的左孩子,对p进行右单旋,对g进行左单旋,c变黑,g变红

	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->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//颜色调整while (parent && parent->_col == RED){//因为parent存在且不是黑色节点,则parent一定不是根,一定存在。Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;//判断叔叔//叔叔存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}//叔叔不存在,或者存在且为黑else{//左子树的左边if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//左子树的右边else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;//判断叔叔//叔叔存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}//叔叔不存在,或者存在且为黑else{//右子树的右边if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//右子树的左边else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}_root->_col = BLACK;}return true;}

我们有可能需要大量判断是否为根节点的情况,我们直接在结尾处加上
_root->_col = BLACK;暴力处理

三、验证红黑树

我们如何进行验证是一颗红黑树呢??
我们从主要的规则入手

🌟根节点是黑色的
🌟不允许出现连续的红色节点
🌟每条路径都包含相同数量的黑色节点

我们只需要判断这三条成立,就能保证最长路径的节点个数不超过最短节点路径的两倍,从而证明这就是一颗红黑树。

根是黑节点很好证明,其他两条呢??

不允许出现连续红色节点,判断一个节点和它的父节点是否都是红色。
这里如果判断这个节点和它的孩子节点会很复杂。

每条路径都包含相同数量的黑色节点,我们可以选择其中一条路径,计算出有多少个黑色节点,从而判断其他路径的黑色节点数量。

bool  IsBalance(){if (_root && _root->_col == RED){cout << "根节点为红色" << endl;return false;}//参考值int level = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){level++;}cur = cur->_left;}return check(_root, 0, level);}//BlackNum不能加引用
bool check(Node* root, int BlackNum, int level)
{if (root == nullptr){if (BlackNum != level){cout << "不包含相同数量的黑色节点" << endl;return false;}return true;}//判断红节点if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "出现连续红节点" << endl;return false;}if (root->_col == BLACK){BlackNum++;}return check(root->_left, BlackNum, level) && check(root->_right, BlackNum, level);
}

四、红黑树与AVL性能比较与应用

性能

红黑树和AVL都是高效的平衡二叉树,增删查改的时间复杂度为·O(logN).
红黑树不追求绝对平衡,只需要保证最长路径不超过最短路径的两倍。相对而言,降低了插入和旋转次数,所以在经常进行增删的结构中比AVL更优,红黑树的实现比较简单,实际应用中红黑树更多。

AVL高度logN,红黑树高度logN*2.红黑树搜索效率相对AVL差一点,但是logN足够小,可以忽略不计。
N=10亿。logN=30.

应用:
1.C++ STL库 – map/set、mutil_map/mutil_set
2.Java 库
3. linux内核
4.其他库

五、总体代码

#pragma onceenum Col
{RED,BLACK
};template<class K,class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Col  _col;pair<K, V>_kv;RBTreeNode(const pair<K, V>kv):_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED),_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);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//颜色调整while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;//判断叔叔//叔叔存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}//叔叔不存在,或者存在且为黑else{//左子树的左边if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//左子树的右边else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;//判断叔叔//叔叔存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}//叔叔不存在,或者存在且为黑else{//右子树的右边if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//右子树的左边else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}_root->_col = BLACK;}return true;}//1.根节点是黑色//2.不包含连续的红色节点//3.每条路径都包含相同黑色节点bool  IsBalance(){if (_root && _root->_col == RED){cout << "根节点为红色" << endl;return false;}//参考值int level = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){level++;}cur = cur->_left;}return check(_root, 0, level);}void Inorder(){_Inorder(_root);}
private://BlackNum不能加引用bool check(Node* root, int BlackNum, int level){if (root == nullptr){if (BlackNum != level){cout << "不包含相同数量的黑色节点" << endl;return false;}return true;}//判断红节点if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "出现连续红节点" << endl;return false;}if (root->_col == BLACK){BlackNum++;}return check(root->_left, BlackNum, level) && check(root->_right, BlackNum, level);}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;}subR->_left= parent;Node* ppnode = parent->_parent;parent->_parent = subR;if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}else{if (parent == ppnode->_left){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;}subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (ppnode == nullptr){_root = subL;_root->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}Node* _root = nullptr;
};

总结

以上就是今天要讲的内容,本文仅仅详细介绍了红黑树的特征,已经模拟实现了插入操作 。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘

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

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

相关文章

zdppy_amauth 实现给角色批量绑定权限

新增接口 api.resp.post("/auth/role_auth", amauth.role.add_auths)如何测试 如何测试能不能给指定的角色批量的添加权限呢&#xff1f; 1、需要新建一个角色2、需要拿到这个角色的ID3、需要新增三个权限4、需要拿到新增的三个权限的ID5、拿着角色ID和权限ID列表…

SSL代码签名最佳实践

代码签名就是软件发布者使用全球可信的证书颁发机构CA颁发的代码签名证书对软件代码进行签名&#xff0c;由此来验证软件开发者的真实身份&#xff0c;确保软件代码的完整性和可信任性。然而&#xff0c;攻击者一直试图渗透代码签名&#xff0c;意将恶意软件嵌入可信代码中。由…

【二叉树】Leetcode 637. 二叉树的层平均值【简单】

二叉树的层平均值 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[3.00000,14.50000,11.00000] 解释&#xff1a…

必应bing国内广告怎样开户投放呢?

企业都在寻找高效、精准的营销渠道以扩大品牌影响力&#xff0c;提升市场占有率&#xff0c;作为全球第二大搜索引擎&#xff0c;微软旗下的必应Bing凭借其卓越的搜索技术和庞大的用户基础&#xff0c;成为了众多企业拓展市场的首选广告平台。在中国&#xff0c;必应Bing广告以…

vuInhub靶场实战系列-DC-6实战

免责声明 本文档仅供学习和研究使用,请勿使用文中的技术源码用于非法用途,任何人造成的任何负面影响,与本人无关。 目录 免责声明前言一、环境配置二、信息收集2.1 主机发现2.1.1 nmap扫描存活主机2.1.2 arp-scan扫描存活主机 2.2 端口扫描2.3 指纹识别2.3.1 尝试指纹识别2.3.…

解决Mac无法上网/网络异常的方法,重置网络

解放方法 1、前往文件夹&#xff1a;/Library/Preferences/SystemConfiguration 2 、在弹窗中输入上边的地址 3 、把文件夹中除了下图未选中的文件全部删掉&#xff0c;删除时需要输入密码 4 、重启mac 电脑就搞定了。

python的一种集成开发工具:PyCharm开发工具

一. 简介 本文简单了解两种 python语言所使用的 集成开发环境&#xff1a; PyCharm、vscode。 python语言学习中&#xff0c;可以任意选中这两个集成开发环境的一种就可以。本文先来简单学习 PyCharm开发工具安装与使用。 二. python的一种集成开发工具&#xff1a;PyChar…

【LeetCode】40. 组合总和 II

组合总和 II 题目描述&#xff1a; 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 示例…

Nginx的https功能

一.HTTPS功能简介 Web网站的登录页面都是使用https加密传输的&#xff0c;加密数据以保障数据的安全&#xff0c;HTTPS能够加密信息&#xff0c;以免敏感信息被第三方获取&#xff0c;所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议&#xff0c;HTTPS其实…

Springboot框架开发与实用篇之热部署 2024详解

开发与实用 手动启动热部署 热部署&#xff08;Hot Deployment&#xff09;指的是在应用程序正在运行的情况下&#xff0c;对其进行更新或修改并将这些变更应用到正在运行的应用程序中的过程。通常情况下&#xff0c;传统的部署方式需要停止应用程序、部署更新&#xff0c;然…

基于51单片机的智能晾衣架设计资料

第三章:硬件单元电路 经过上述分析明确了本次设计的主要目标,为了实现晾衣自身能够完成对外界数据的采集与分析,集成控制环节我们采用了ATMEL公司生产的AT89C52单片机,与市面上的其他嵌入式控制单元相比较在体积与功耗方面都相当出色。此次设计主要突破在于设计合理的控制电…

最短路问题

最短路问题是图论里非常经典的一个考点 接下来着重讲述五种求最短路的算法&#xff1a;朴素版dijkstra算法、堆优化版的dijkstra算法、bellman-ford算法、spfa算法、floyd算法 总体思维导图&#xff1a; 总体思路&#xff1a; 最短路分为两大类 { 在以下给出的时间复杂度中n…

学习笔记——路由网络基础——静态路由(static)

三、静态路由(static) 1、静态路由 (1)定义 静态路由(Static)&#xff1a;由管理员手动配置和维护的路由。静态路由配置简单&#xff0c;被广泛应用于网络中。此外还可以实现负载均衡和路由备份。 静态路由默认优先级为60&#xff0c;如果想在多条静态路由中让某条路由优选…

uniapp小程序开发 | 从零实现一款影视类app (后台接口实现,go-zero微服务的使用)

uniapp小程序开发实战系列&#xff0c;完整介绍从零实现一款影视类小程序。包含小程序前端和后台接口的全部完整实现。系列连载中&#xff0c;喜欢的可以点击收藏。 该篇着重介绍获取轮播图后台接口和获取正在热映电影的两个后台接口的实现。 后台服务使用golang&#xff0c;…

MySQL—多表查询—自连接

一、引言 自连接&#xff0c;顾名思义就是自己连接自己。 自连接的语法结构&#xff1a; 表 A 别名 A join 表 A 别名 B ON 条件 ...; 注意&#xff1a; 1、这种语法有一个关键字&#xff1a;join 2、自连接查询可以是内连接的语法&#xff0c;可以是外连接的语法&#xff08…

【游戏】Goc赚钱模拟器1.0版

Hello!大家好&#xff0c;我是学霸小羊&#xff0c;今天分享一个Goc游戏。 //注&#xff1a;以下代码为Goc原创代码。 大家可以在下面网址写入代码www.51goc.com慧通教育http://www.51goc.com注&#xff1a;Goc编辑器路径&#xff1a; www.51goc.com ➡ 登录 ➡ 游客登陆 ➡…

Three.js加入到可视化大屏,看看能否惊艳到你?

three.js 在可视化大屏上可以实现各种三维场景和动画效果&#xff0c;可以根据具体需求进行定制化开发&#xff0c;并结合其他技术&#xff0c;如数据可视化、交互设计等&#xff0c;实现更加丰富的可视化效果。 three.js 是一个基于 WebGL 的 JavaScript 3D 库&#xff0c;可…

循迹模块之循迹小车

1.TCRT5000传感器 TCRT5000传感器的红外发射二极管 不断发射红外线 1.1 当发射出的红外线没有被反射回来或被反射回来但强度不够大时&#xff0c; 红外接收管一直处于关断状态&#xff0c;此时模块的输出端为高电平&#xff0c;指示二极管一直处于熄灭状态 1.2 当被检测物体…

跳跃游戏二

方法一&#xff1a;&#xff08;双指针法&#xff09;此题参考跳台阶问题&#xff0c;题目要求求到达最后一个点的最小跳跃次数&#xff0c;那么我们就可以从最后一个往前推&#xff0c;先看谁能离得最远&#xff0c;并且能跳到最后一个。假设i位置是离最后一个位置最远&#x…

【新书上市】图像画质算法与底层视觉技术

图书主页&#xff1a;https://book.douban.com/subject/36895899/ 购买链接&#xff1a;https://item.jd.com/10105601481762.html 内容介绍 本书主要介绍了图像画质相关的各类底层视觉任务及其相关算法&#xff0c;重点讲解了去噪、超分辨率、去雾、高动态范围、图像合成与图…