[C++][数据结构]红黑树的介绍和模拟实现

前言

之前我们简单学习了一下搜索树和平衡搜索树,今天我们来学习一下红黑树

简介

概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 (即不能有连续的红色节点)
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点NIL节点),是为了方便第四点

实现

基本结构

我们默认新插入的节点为红色,因为根据第四条规则,黑色节点不能轻易添加

template<class K, class V>
struct RBTreeNode
{using Node = RBTreeNode<K, V>;Node* _left;Node* _right;Node* _parent;pair<K, V> _kv;Color _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv),_col(RED){}
};

插入

插入节点最终的颜色的关键是看叔叔节点

  1. 原因:
    我的父节点是红的,那我的爷爷节点一定是黑的,并且爷爷一定存在(因为根节点不为红色),所以就是看叔叔节点的颜色来判断插入结点的颜色

  2. 修改原则:
    a. 叔叔存在且为红,则将父节点和叔叔节点变黑再将爷爷节点变红,

    • 更新cur节点为爷爷节点,直到更新到根节点

在这里插入图片描述

b. 叔叔为黑或叔叔不存在

  • 叔叔不存在,说明cur是新增的节点,直接就是红色
  • 叔叔为黑,那么cur不可能是新增,不然parent不满足第四条规则

针对第二点,我们需要考虑会不会最长路径超出最短路径的二倍或者面临下面这种情况(出现连续的红色节点且无法修改),那么这里我们就必须要进行旋转+变色。
(与AVL的旋转相同)
在这里插入图片描述(好像有个小规律:parent始终为黑色)

旋转的规则:
p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p变黑,g变红

在这里插入图片描述

c. cur为红,p为红,g为黑,u不存在/u为黑
更新规则:

  • p为g的左孩子,cur为p的右孩子:左右双旋+变色
  • p为g的右孩子,cur为p的左孩子:右左双旋+变色
bool Insert(const pair<K, V>& kv)
{//1.按照搜索树规则插入:先找到合适的位置,然后链接if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}//如果树为空,特殊判断Node* parent = nullptr;Node* cur = _root;while (cur != nullptr){if (cur->kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->kv.first < kv.first){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(kv);		//默认新节点是红色if (parent->kv.first > kv.first){parent->_right = cur;}else{parent->_right = cur;}cur->_parent = parent;//持续更新parent,直到更新到根节点while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//根据父亲,找叔叔if (parent == grandfather->_left){Node* uncle = grandfather->_right;//情况一:叔叔存在且为红if (uncle != nullptr && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//更新节点cur = grandfather;parent = cur->_parent;}//情况二:叔叔不存在或者为黑else{if (cur == parent->_left){//       g//    p    u// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//       g//    p     u//      cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle != nullptr && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//更新节点cur = grandfather;parent = cur->_parent;}else{//结构图//      g//   u     p//            cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//   u     p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}

验证

要验证这棵树是不是红黑树,要根据他的规则去判断

  1. 根节点是黑色的
  2. 不能有连续的红色节点
  3. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点

所以我们可以根据这三点去判断

	bool Check(Node* cur){if (cur == nullptr)return true;if (cur->_col == RED && cur->_parent->_col == RED)//第二点//根据孩子找父亲{cout << cur->_kv.first << "存在连续的红色节点" << endl;return false;}return Check(cur->_left)&& Check(cur->_right);}bool IsBalance(){if (_root && _root->_col == RED)return false;//第一点return Check(_root);}

以上是对于第一点和第二点的验证,对于第三点,我们要求每两条路径的黑色节点数量,判断是否相等,
我们可以用老方法:增加参数个数

bool Check(Node* cur, int blackNum, int refBlackNum){if (cur == nullptr){if (refBlackNum != blackNum){throw("黑色节点的数量不相等");}return true;}if (cur->_col == RED && cur->_parent->_col == RED){cout << cur->_kv.first << endl;throw("存在连续的红色节点");}if (cur->_col == BLACK)++blackNum;return Check(cur->_left, blackNum, refBlackNum)&& Check(cur->_right, blackNum, refBlackNum);}bool IsBalance(){if (_root && _root->_col == RED)return false;int refBlackNum = 0;Node* cur = _root;while (cur){if(cur->_col == BLACK)refBlackNum++;cur = cur->_left;}return Check(_root, 0, refBlackNum);}

结语

红黑树的实现还没写完,下一篇文章会介绍红黑树的迭代器的简单实现和map和set是如何封装的 ,下次见~

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

地图产业的困局与破局:高精地图“上车”难 轻量化渐成主流方案 | 最新快讯

《科创板日报》5月3日讯&#xff08;编辑 邱思雨&#xff09; 近期&#xff0c;特斯拉与百度的“绯闻”成为智驾、地图行业的焦点。 有媒体消息称&#xff0c;特斯拉将与百度地图独家深度定制车道级高辅地图。《科创板日报》记者也获悉&#xff0c;自5月1日起&#xff0c;百度…

pygame鼠标绘制

pygame鼠标绘制 Pygame鼠标绘制效果代码 Pygame Pygame是一个开源的Python库&#xff0c;专为电子游戏开发而设计。它建立在SDL&#xff08;Simple DirectMedia Layer&#xff09;的基础上&#xff0c;允许开发者使用Python这种高级语言来实时开发电子游戏&#xff0c;而无需被…

ESXi8 中FreeBSD启动失败记录

一天突然发现ESXi8 中的FreeBSD启动失败&#xff0c;且自动挂载了FreeBSD的安装光盘&#xff0c;进入安装步骤。 惊了一身冷汗。 到虚拟主机设置里&#xff0c;发现引导选项里面&#xff0c;选择应当用来引导虚拟机的固件为EFI&#xff0c;原来是前段时间手残修改了&#xff0…

图片倾斜矫正处理(Hough Transform)

目录 倾斜矫正原理及实现方式Canny边缘检测非极大值抑制霍夫变换 倾斜矫正原理及实现方式 代码连接&#xff1a;https://github.com/shuyeah2356/Image-Angel-correction/tree/main 倾斜矫正的实现原理&#xff1a; 使用霍夫变换检测图片中的直线&#xff1b; 计算直线与水平方…

TCP四次挥手分析

TCP四次挥手分析 概念过程分析为什么连接的时候是三次握手&#xff0c;关闭的时候却是四次握手&#xff1f;为什么要等待2MSL&#xff1f; 概念 四次挥手即终止TCP连接&#xff0c;就是指断开一个TCP连接时&#xff0c;需要客户端和服务端总共发送4个包以确认连接的断开。 在…

机器视觉系统-条形光源安装位置计算

使用条形光对反光材质物体打光时&#xff0c;常常出现强烈的光斑反射&#xff0c;影响图像处理。如果不想图像中出现光源的光斑&#xff0c;可以通过计 算得出条形光源的安装范围。 检则PCB板上的二维码字符&#xff0c;使用两个条形光打光的效果图 以及等效模型&#xff1a; …

机器学习:深入解析SVM的核心概念【二、对偶问题】

对偶问题 **问题一&#xff1a;什么叫做凸二次优化问题&#xff1f;而且为什么符合凸二次优化问题&#xff1f;**为什么约束条件也是凸的半空间&#xff08;Half-Space&#xff09;凸集&#xff08;Convex Set&#xff09;半空间是凸集的例子SVM 约束定义的半空间总结 **问题二…

领域驱动设计(DDD)笔记(三)后端工程架构

文章链接 领域驱动设计(DDD)笔记(一)基本概念-CSDN博客领域驱动设计(DDD)笔记(二)代码组织原则-CSDN博客领域驱动设计(DDD)笔记(三)后端工程架构-CSDN博客前导 领域驱动设计(Domain Driven Design,简称DDD)是业内主导的业务工程理论。它在各中权威人士被广泛讨论…

ArrayList应用

1.简单的洗牌算法 基本要求&#xff1a; 人数为3个人没人轮流抽一张牌&#xff0c;抽五轮&#xff0c;也就是每人五张牌去除大小王&#xff0c;一共52张牌&#xff0c;要求牌打乱顺序 思路&#xff1a; 创建Card对象&#xff0c;有花色和牌面值两个成员属性生成一副扑克牌&…

ICode国际青少年编程竞赛- Python-1级训练场-for循环练习

ICode国际青少年编程竞赛- Python-1级训练场-for循环练习 1、 for i in range(3):Dev.step(4)Dev.turnLeft()2、 for i in range(3):Dev.step(2)Dev.turnRight()Dev.step(2)Dev.turnLeft()3、 for i in range(3):Dev.step(2)Dev.turnRight()Dev.step(2)Dev.turnLeft()4、 for…

痉挛性斜颈患者早上运动还是下午运动更合适?选对了让治疗更简单!【北京仁爱堂】

对于痉挛性斜颈患者来说&#xff0c;选择合适的运动时间对于治疗的效果和舒适度至关重要。那么&#xff0c;痉挛性斜颈患者早上运动还是下午运动更合适呢&#xff1f;本文将从多个角度对此进行分析&#xff0c;帮助患者找到最适合自己的运动时间。 首先&#xff0c;我们需要了…

网络安全的重要性及人才需求

安全现在是大趋势&#xff0c;说是铁饭碗也不为过&#xff0c;就业前景好&#xff0c;方向多比传统计算机行业就业舒服点。但是大厂依然是985&#xff0c;211的天下&#xff0c;是双非能进大厂的&#xff0c;只是凤毛麟角。前提是你的能力可以让公司忽略你的学历。 以2023年为…

HTML_CSS学习:常用文本属性

一、文本颜色 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>文本颜色</title><style>div{font-size: 90px;}.atguigu1{color: #238c20;}.atguigu2{color: rgb(2…

文心一言 VS 讯飞星火 VS chatgpt (250)-- 算法导论18.2 3题

三、请说明如何在一棵 B 树中找出最小关键字&#xff0c;以及如何找出某一给定关键字的前驱。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 在B树&#xff08;也称为B-tree&#xff09;中&#xff0c;最小的关键字总是位于最左边的叶子节点的最左边的元素。要…

中国科学院南京地理与湖泊研究所张科研究员团队联合多家单位科学家在PNAS发文:湖泊沉积物解锁长三角太湖流域可持续发展之道

文章简介 论文名称&#xff1a;Transient social–ecological dynamics reveal signals of decoupling in a highly disturbed Anthro-pocene landscape&#xff08;瞬态社会生态动力学揭示了高度干扰的人类景观中解耦的信号&#xff09; 第一作者及通讯作者&#xff1a;林琪…

波形发生器设计(频率、占空比、幅值可调)

一、正弦波信号发生器 1.电路原理图&#xff1a; 2.原理&#xff1a; 采用了文氏电桥的方法&#xff0c;通过自激振荡的方式出波。 其中R6,C1,R2,C2构成正反馈支路&#xff0c;令R1R2R,C1C2C&#xff0c;可以计算出正弦波的振荡频率f1/2πRC。将文氏电路的电容值固定&#xff0…

《21天学通C++》(第十五章)标准模板库简介

本章简单介绍STL容器、迭代器和算法的基本概念&#xff0c;之后几章会分别详述 1.STL容器 STL容器是STL中用于存储集合数据的组件&#xff0c;它们可以被看作是模板类&#xff0c;允许开发者定义特定类型的容器发&#xff0c;这里按照C11标准分为四类&#xff1a;顺序容器、关…

信息系统项目管理师0082:项目基础(6项目管理概论—6.2项目基本要素—6.2.1项目基础)

点击查看专栏目录 文章目录 6.2项目基本要素6.2.1项目基础1.独特的产品、服务或成果2.临时性工作3.项目驱动变更4.项目创造业务价值5.项目启动背景记忆要点总结6.2项目基本要素 6.2.1项目基础 项目是为创造独特的产品、服务或成果

XYCTF2024 RE ez unity 复现

dll依然有加壳 但是这次global-metadata.dat也加密了&#xff0c;原工具没办法用了&#xff0c;不过依然是可以修复的 a. 法一&#xff1a;frida-il2cpp-bridge 可以用frida-il2cpp-bridge GitHub - vfsfitvnm/frida-il2cpp-bridge: A Frida module to dump, trace or hijac…

STM32入门_江协科技_3~4_OB记录的自学笔记_软件安装新建工程

3. 软件安装 3.1. 安装Keil5 MDK 作者的资料下载的连接如下&#xff1a;https://jiangxiekeji.com/download.html#32 3.2. 安装器件支持包 因为新的芯片层出不穷&#xff0c;所以需要安装Keil5提供的器件升级版对软件进行升级&#xff0c;从而支持新的芯片&#xff1b;如果不…