当前位置: 首页 > article >正文

C++-第十三章:红黑树

目录

第一节:红黑树的特征

第二节:实现思路

        2-1.插入

                2-1-1.unc为红

                2-1-2.cur为par的左子树,且par为gra的左子树(cur在最左边)

                        2-1-2-1.unc不存在

                        2-1-2-2.unc为黑

                2-1-3.cur为par的右子树,且par为gra的右子树(cur在最右侧)

                        2-1-3-1.unc不存在

                        2-1-3-2.unc为黑

                2-1-4.cur为par的左子树,且par为gra的右子树(cur在左内侧)

                        2-1-4-1.unc无关

                2-1-5.cur为par的右子树,且par为gra的左子树(cur在右内侧)

                        2-1-5-1.unc无关

                2-1-6.par为黑

        总结:

第三节:代码实现

        2-1.Node类

        2-2.RBTree类

 Gitee:红黑树 · 转调/C++ - 码云 - 开源中国

第四节:测试

下期预告:


第一节:红黑树的特征

        红黑树并没有AVL树那种严格的平衡因子限制,它只保证最长路径的长度不会超过最短路径的两倍。

        红黑树的特征如下:

        (1)节点分为两种"红"与"黑"

        (2)根节点是"黑"

        (3)"红"节点的孩子都是"黑"节点——不存在连续的"红"节点

        (4)对于每个节点,从该节点开始,到叶子节点结束,的所有简单路径均包含相同数量的"黑"节点

        上述的"红"与"黑"并不是指颜色,只是区分两种节点的方法。

第二节:实现思路

        2-1.插入

        插入时优先将新增节点设置为红色,否则因为规则(4)的制约,黑节点会影响多条路径。

        假如新插入的节点为cur,它的父亲为par,爷爷为gra,父亲的兄弟为unc,那么一共有多种情况。

        当par为红时,因为规则(2),一定有一个黑色gra,只有一个unc是未知的

        所以先讨论par为红时的情况:

                2-1-1.unc为红

                                

        这种情况就违反了规则(3),所以要进行改变:

        将par、unc变为黑色,gra变为红色。

                ​​​​​​​        ​​​​​​​        

        此时这个子树已经符合红黑树的规则了,但是对于gra之上的节点来说,变红的gra等价于插入了一个红色的cur,所以又要将gra作为cur,继续向上调整,直到根或者par为"黑"。这是一种递归的思想。

        其次,如果gra就是root,那么根据规则(2)还需要把gra变为黑色。

                2-1-2.cur为par的左子树,且par为gra的左子树(cur在最左边)

                        2-1-2-1.unc不存在

                                        

        此时要在gra和par之间进行右单旋,旋转方式和AVL树的右单旋一致。

        然后将gra变红,par变黑。

        因为par已经变成黑色了,所以不再向上更新。

                        2-1-2-2.unc为黑

                                

        此时也执行与2-1-2-1相同的操作,即在par和gra之间进行右单旋;

        然后将gra变红,par变黑。

        它也不用再向上更新了。

        总结: cur在最左边时,gra和par右单旋+变色。

                2-1-3.cur为par的右子树,且par为gra的右子树(cur在最右侧)

                        2-1-3-1.unc不存在
                        2-1-3-2.unc为黑

        这两种情况和2-1-2的位置情况相反,cur从最左变到了最右,处理方法也类似,只是把右单旋操作变成了左单旋操作

        总结:cur在最右侧时,gra和par左单旋+变色 

                2-1-4.cur为par的左子树,且par为gra的右子树(cur在左内侧)

                        2-1-4-1.unc无关

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

        此时cur在左内测,先对par和cur使用一次左单旋:

        与2-1-3相比,虽然par和cur的位置不一样,但是par和cur都是红色,将par视作cur,cur视作par之后,它就变成了2-1-3的情况了。

        所以接下来进行右单旋+变色即可。

        之前就行进行了一次左单旋,所以cur在左内侧的情况使用右左双旋+cur和gra互变颜色

                2-1-5.cur为par的右子树,且par为gra的左子树(cur在右内侧)

                        2-1-5-1.unc无关

        此时cur在右内侧,所以使用右左双旋+cur和gra互变颜色

        最后是par为黑的情况。

                2-1-6.par为黑

        此时不用做任何改变,因为cur本来就是红色,不违反任何规则。

        总结:

        综上,其实一共就两种情况:

        (1)par为红时:一定有一个黑色的gra,unc为变量

                a.unc为红:

                          Ⅰ.par、unc变黑,gra变红 继续向上调整

                b.unc为黑/不存在:

                          Ⅰ.cur在外侧:单旋+par、gra变色  然后直接结束

                          Ⅱ .cur在内侧:双旋+cur、gra变色  然后直接结束

        (2)par为黑时:直接结束     

        同搜索二叉树,使用替代法:C++-第十章:搜索二叉树-CSDN博客

第三节:代码实现

        将总结整理成代码。

        2-1.Node类

         使用枚举enum对节点进行"红"、"黑"分类,并且节点初始为"红":

	enum NodeType{RED = 0,BLACK = 1};template<class T>class Node{public:Node<T>* _left = nullptr;Node<T>* _right = nullptr;Node<T>* _parent = nullptr;T val;NodeType _type = RED;};

        2-2.RBTree类

        这里直接给出核心代码:

namespace zd
{template<class T>class RBTree{public:// 插入函数void Insert(const T& val){// 没有节点就初始化根节点if (_root == nullptr){_root = new Node<T>;_root->_val = val;// 记得根的颜色一定是黑_root->_type = BLACK;}Node<T>* cur = _root;Node<T>* parent = nullptr;while (cur){if (cur->_val < val){parent = cur;cur = cur->_right;}else if (cur->_val > val){parent = cur;cur = cur->_left;}else{// 不允许存储重复的valreturn;}}cur = new Node<T>;cur->_val = val;if (parent->_val > cur->_val){parent->_left = cur;	}else{parent->_right = cur;}cur->_parent = parent;// 使红黑树符合规则Balance(cur);}void _Print(Node<T>* root){if (root == nullptr) return;_Print(root->_left);std::cout << root->_val << " ";_Print(root->_right);}// 中序遍历打印void Print(){_Print(_root);}private:// 平衡红黑树void Balance(Node<T>* cur){// 只有上一个gra才可以递归到根,将根变为黑色并退出if (cur == _root){_root->_type = BLACK;return;}Node<T>* par = cur->_parent;if (par->_type == BLACK) // 黑色直接结束{return;}else // 红色{Node<T>* gra = par->_parent;Node<T>* unc;if (par == gra->_left)unc = gra->_right;elseunc = gra->_left;// unc存在且为红if (unc && unc->_type == RED){// par、unc变黑par->_type = unc->_type = BLACK;// gra变红gra->_type = RED;// 递归调用自己,继续向上调整Balance(gra);}// unc为黑/不存在 && cur在左外侧else if (cur == par->_left && par == gra->_left){RotateR(gra); // 右单旋// 变色par->_type = BLACK;gra->_type = RED;return;}// unc为黑/不存在 && cur在右外侧else if (cur == par->_right && par == gra->_right){RotateL(gra); // 左单旋// 变色par->_type = BLACK;gra->_type = RED;return;}// unc为黑/不存在 && cur在左内侧else if (cur == par->_right && par == gra->_left){RotateLR(gra); // 左右双旋// 变色cur->_type = BLACK;gra->_type = RED;return;}// unc为黑/不存在 && cur在右内侧else if (cur == par->_left && par == gra->_right){RotateRL(gra); // 右左双旋// 变色cur->_type = BLACK;gra->_type = RED;return;}}}// 右单旋void RotateR(Node<T>* gra){Node<T>* par = gra->_left;gra->_left = par->_right;if (gra->_left)gra->_left->_parent = gra;par->_right = gra;// 正确连接par和gra的父亲if (gra == _root){_root = par;}else{if (gra->_parent->_left == gra)gra->_parent->_left = par;elsegra->_parent->_right = par;}par->_parent = gra->_parent;gra->_parent = par;}// 左单旋void RotateL(Node<T>* gra){Node<T>* par = gra->_right;gra->_right = par->_left;if (gra->_right)gra->_right->_parent = gra;par->_left = gra;// 正确连接par和gra的父亲if (gra == _root){_root = par;}else{if (gra->_parent->_left == gra)gra->_parent->_left = par;elsegra->_parent->_right = par;}par->_parent = gra->_parent;gra->_parent = par;}// 左右双旋void RotateLR(Node<T>* gra){Node<T>* par = gra->_left;RotateL(par);RotateR(gra);}// 右左双旋void RotateRL(Node<T>* gra){Node<T>* par = gra->_right;RotateR(par);RotateL(gra);}private:Node<T>* _root = nullptr;};
};

        我们还需要一个函数来验证红黑树,验证规则如下:

        (1)根为黑

        (2)任意红色节点的父亲为黑

        (3)以任意节点为起点,到叶子节点的路径上的黑色节点数量相等

                方法:每个节点记录最左路径上的黑色节点数量为标准,其他路径和它不一样,那就说明不是红黑树。

        

        将上述规则实现成代码:

		// 验证红黑树bool IsRBTree(){// 验证根的颜色if (_root->_type == RED)return false;// 检查红节点的父亲都是黑节点bool ret1 = RedOfBlack(_root);// 遍历树,每个节点检查自己的路径上的黑色节点是否相同bool ret2 = BlackIsEq(_root);if (ret1 == false)printf("出现连续红\n");if (ret2 == false)printf("黑色数量不一致\n");return ret1 && ret2;}// 检查连续红节点bool RedOfBlack(Node<T>* root){if (root == nullptr) return true;// 验证红色节点的父亲为黑色if (root->_type == RED){if (root->_parent->_type == RED)return false;}return RedOfBlack(root->_left) && RedOfBlack(root->_right);}// 检查路径黑节点数量bool BlackIsEq(Node<T>* root){if (root == nullptr) return true;// 获得最左路径黑节点数量int Bc = 0;Node<T>* cur = root;while (cur){if (cur->_type == BLACK)Bc++;cur = cur->_left;}int otherPath = root->_type == BLACK ? 1 : 0; // 其他路径的节点数量return _BlackIsEq(root->_left,Bc,otherPath) && _BlackIsEq(root->_right, Bc, otherPath);}bool _BlackIsEq(Node<T>* root, int Bc, int oP){if (root == nullptr){if (Bc == oP) return true;return false;}if (root->_type == BLACK){oP++;}return _BlackIsEq(root->_left, Bc, oP) && _BlackIsEq(root->_right,Bc,oP);}

 Gitee:红黑树 · 转调/C++ - 码云 - 开源中国

第四节:测试

        生成多个随机数进行测试即可:

#include "RBTree.hpp"
#include <time.h>
int main()
{zd::RBTree<int> tree;srand(time(nullptr));int i = 100;while (i--){int x = rand();tree.Insert(x);}tree.Print();std::cout << tree.IsRBTree() << std::endl;return 0;
}

下期预告:

        第十四章将学习哈希表的原理,并自己实现一个简单的哈希表。

相关文章:

C++-第十三章:红黑树

目录 第一节&#xff1a;红黑树的特征 第二节&#xff1a;实现思路 2-1.插入 2-1-1.unc为红 2-1-2.cur为par的左子树&#xff0c;且par为gra的左子树(cur在最左边) 2-1-2-1.unc不存在 2-1-2-2.unc为黑 2-1-3.cur为par的右子树&#xff0c;且par为gra的右子树(cur在最右侧) 2-…...

推荐3个背景渐变色的wordpress主题

干净、清爽、背景渐变色的wordpress企业主题 ​ 服务类公司wordpress企业主题https://www.jianzhanpress.com/?p8255 红色大气的wordpress企业主题&#xff0c;适合服务行业的公司搭建企业官方网站使用。 ​ wordpress询盘型独立站主题https://www.jianzhanpress.com/?p8258…...

Scrapy:隧道代理中移除 Proxy-Authorization 的原理解析

隧道代理中移除 Proxy-Authorization 的原理解析 背景 在 Scrapy 的 HTTP 下载处理中&#xff0c;当使用隧道代理&#xff08;TunnelingAgent&#xff09;时&#xff0c;会移除请求头中的 Proxy-Authorization。这个操作看似简单&#xff0c;但背后有着重要的安全考虑和技术原…...

Qt for Android下QMessageBox背景黑色、文字点击闪烁

最近在基于Qt开发安卓应用的时候,在红米平板上默认QMessageBox出现之后,背景黑色,并且点击提示文字会出现闪烁,影响用户体验。 问题分析 1、设置QMessageBox样式,设置背景色、文字颜色,如下所示: QMessageBox {background: white;color: white; } 尝试之后,问题仍存…...

Docker数据卷操作实战

什么是数据卷 数据卷 是一个可供一个或多个容器使用的特殊目录&#xff0c;它绕过 UFS&#xff0c;可以提供很多有用的特性: 数据卷 可以在容器之间共享和享用对 数据卷 的修改立马生效对 数据卷 的更新&#xff0c;不会影响镜像数据卷 默认会一直存在&#xff0c;即时容器被…...

nginx 动态计算拦截非法访问ip

需求&#xff1a;在Nginx上实现一个动态拦截IP的方法&#xff0c;具体是当某个IP在1分钟内访问超过60次时&#xff0c;将其加入Redis并拦截&#xff0c;拦截时间默认1天。 技术选型&#xff1a;使用NginxLuaRedis的方法。这种方案通过Lua脚本在Nginx处理请求时检查Redis中的黑…...

微信小程序-二维码绘制

wxml <view bindlongtap"saveQrcode"><!-- 二维码 --><view style"position: absolute;background-color: #FFFAEC;width: 100%;height: 100vh;"><canvas canvas-id"myQrcode" style"width: 200px; height: 200px;ba…...

Node.js与MySQL的深入探讨

Node.js与MySQL的深入探讨 引言 Node.js,一个基于Chrome V8引擎的JavaScript运行时环境,以其非阻塞、事件驱动的方式在服务器端应用中占据了一席之地。MySQL,作为一款广泛使用的开源关系型数据库管理系统,凭借其稳定性和高效性,成为了许多应用的数据库选择。本文将深入探…...

【10】RUST的迭代器与闭包

文章目录 闭包(Closures)定义捕获方式:迭代器(Iterator)核心方法:创建方式:适配器(Adapter)常见适配器及示例消费方法(Consumer)所有权与引用处理性能与惰性求值闭包(Closures) 类比C++里的lambda表达式 闭包是能够捕获其所在环境变量的匿名函数,支持灵活的类型推…...

Fiddler 的安装与使用

目录 1、Fiddler 的安装2、Fiddler 的使用 1、Fiddler 的安装 通过Fiddler 官网进行下载&#xff08;下载免费的经典版本&#xff09;&#xff0c;填写用途、邮箱、国家信息即可开始下载。 Fiddler 官网下载链接 双击安装包即可进行安装&#xff0c;显示以下界面说明安装成功。…...

Hadoop架构详解

Hadoop 是一个开源的分布式计算系统&#xff0c;用于存储和处理大规模数据集。Hadoop 主要由HDFS&#xff08;Hadoop Distributed File System&#xff09;、MapReduce、Yarn&#xff08;Jobtracker&#xff0c;TaskTracker&#xff09;三大核心组件组成。其中HDFS是分布式文件…...

清华大学DeepSeek文档下载,清华大学deepseek下载(完成版下载)

文章目录 前言一、清华大学DeepSeek使用手册下载二、清华大学DeepSeek使用手册思维导图 前言 这是一篇关于清华大学deepseek使用手册pdf的介绍性文章&#xff0c;主要介绍了DeepSeek的定义、功能、使用方法以及如何通过提示语设计优化AI性能。以下是对这些核心内容的简要概述&…...

Hadoop第2课(伪分布式集群的搭建)

jdk和hadoop安装包&#xff1a; hadoop-2.9.2.t......等2个文件官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘 1、用XFTP发送hadoop安装包和jdk到/home/hadoop/目录下&#xff08;hadoop用户的主目录&#xff09; 2、解压jdk安装包到~目录 卸载jdk的命令&#xff1a;r…...

DeepSeek开源周第二弹:DeepEP如何用RDMA+FP8让MoE模型飞起来?

一、引言&#xff1a;MoE模型的通信瓶颈与DeepEP的诞生 在混合专家&#xff08;MoE&#xff09;模型训练中&#xff0c;专家间的全对全&#xff08;All-to-All&#xff09;通信成为性能瓶颈。传统方案在跨节点传输时带宽利用率不足50%&#xff0c;延迟高达300μs以上。DeepSee…...

IoT 测试:智能互联时代的质量保障

一、IoT(物联网)概述 物联网(Internet of Things, IoT)指的是将各种设备、传感器和系统连接到互联网&#xff0c;实现数据采集、传输、处理和智能化应用。随着 5G、云计算、人工智能等技术的发展&#xff0c;IoT 在智能家居、工业自动化、医疗健康、智能交通等领域的应用日益广…...

使用Crawlee可破题js渲染采集数据

使用 Crawlee 实现自动化爬虫流程 1. Crawlee 简介 Crawlee 是一个强大的爬虫框架&#xff0c;用于快速构建和维护可靠的爬虫。它支持多种爬虫类型&#xff0c;包括基于 Cheerio 和 Playwright 的爬虫&#xff0c;能够高效处理静态和动态网页。 2. 项目目标 通过自动化脚本实…...

短连接服务器压测-wrk

背景 由于业务需要我们从原来的 长连接 转为 短连接&#xff0c;提高单服同时在线人数。 老压测 在服务器编写机器人&#xff0c;编写一部分客户端逻辑&#xff08;这里如果客户端严格使用mvc 模式&#xff0c;其实可以把 view 层换为 服务器测试代码层&#xff0c;而一般不…...

DAV_postgresql_2-user_role

数据库角色用来管理数据库访问权限&#xff0c;简化权限的管理 用户和角色在整个数据库集簇中是全局性的&#xff0c;不是针对某个单一数据库&#xff0c;只要有足够的权限&#xff0c;用户可以访问所有数据库的对象。 数据库用户可以分为两类 超级用户 -- postgres 普通…...

php 获取head参数

php 获取head参数 在PHP中&#xff0c;获取HTTP头部&#xff08;head&#xff09;参数可以通过不同的方式实现&#xff0c;下面为你详细介绍几种常见的方法。 1. 使用$_SERVER超全局变量 $_SERVER 是PHP中的一个超全局变量&#xff0c;它包含了诸如头信息、路径、脚本位置等…...

Fiddler在Windows下抓包Https

文章目录 1.Fiddler Classic 配置2.配置浏览器代理自动代理手动配置浏览器代理 3.抓取移动端 HTTPS 流量&#xff08;可选&#xff09;解决抓取 HTTPS 失败问题1.Fiddler证书过期了 默认情况下&#xff0c;Fiddler 无法直接解密 HTTPS 流量。需要开启 HTTPS 解密&#xff1a; 1…...

SQLite数据库从0到1

SQLite SQLite基础知识 SQLite数据库功能特性&#xff1a;ACID事务&#xff1b;支持数据库大小至2TB&#xff1b;足够小&#xff0c;大致13万行C代码4MB左右&#xff1b;存储在单一磁盘文件中的完整数据库。独立&#xff0c;无额外依赖。源码完全开源。支持多种编程语言&#…...

PMP项目管理—整合管理篇—7.结束项目或阶段

文章目录 基本信息过程4W1HITTO输入工具与技术输出 收尾过程组项目收尾&#xff08;结束项目或阶段&#xff09;行政收尾/管理收尾 合同收尾&#xff08;结束采购&#xff09; 最终报告 基本信息 项目无论何因何时终止&#xff0c;都必须用结束项目或阶段过程来正式关闭。通过…...

计算机网络基础简答题资料(对口高考)

1、什么是计算机网络&#xff1f;计算机网络的功能有哪些&#xff1f; 答案&#xff1a;计算机网络&#xff0c;是指将分布在不同地理位置、具有独立功能的多台计算机及其外围设备&#xff0c;通过通信设备和通信线路连接起来&#xff0c;在网络操作系统、网络管理软件及网络通…...

Java语法基础知识点1

目录 一、数组 1.1数组的初始化&#xff1a; 1.2数组的遍历方法&#xff1a; 1.3数组的常见使用方法&#xff1a; 二、类和对象 2.1构造方法&#xff1a; 2.2this关键字: 三、封装 3.1访问限定符&#xff1a; 3.2static关键字&#xff1a; 3.3代码块&#xff1a; 一…...

2025年跟上AI新时代:带AI人工智能的蜜罐系统T-Pot

T-Pot是一个集成式、可选分布式的、支持多架构&#xff08;amd64、arm64&#xff09;的蜜罐平台&#xff0c;它支持20多种蜜罐&#xff0c;并提供了使用Elastic Stack的无数可视化选项、动态实时攻击地图以及众多安全工具&#xff0c;以进一步提升蜜罐系统体验。源码地址&#…...

【新手入门】SQL注入之盲注

一、引言 在我们的注入语句被带入数据库查询但却什么都没有返回的情况我们该怎么办? 例如应用程序返回到一个"通用的"的页面&#xff0c;或者重定向一个通用页面(可能为网站首页)。这时&#xff0c;我们之前学习的SQL注入的办法就无法使用了。这种情况我们称之为无…...

python-leetcode-分割等和子集

416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; class Solution:def canPartition(self, nums: List[int]) -> bool:total sum(nums)if total % 2 ! 0:return Falsetarget total // 2dp [False] * (target 1)dp[0] Truefor num in nums:for j in range(tar…...

【大模型+知识图谱】大模型与知识图谱融合:技术演进、实践应用与未来挑战

【大模型+知识图谱】大模型与知识图谱融合:技术演进、实践应用与未来挑战 大模型与知识图谱融合:技术演进、实践应用与未来挑战引言:为什么需要融合?一、技术融合的三重路径1.1 知识图谱增强大模型1.2 大模型赋能知识图谱1.3 协同推理框架二、工业级应用场景落地2.1 智能问…...

python 视频网站爬虫教程,爬虫入门教程(付安装包)

文章目录 前言1. 环境准备Python安装选择Python开发环境安装必要库 2. 了解目标网站3. 发送请求获取页面内容4. 解析页面内容&#xff0c;提取视频链接5. 下载视频6. 处理反爬机制7. 完整代码示例注意事项 前言 以下为你生成一份 Python 视频网站爬虫教程&#xff0c;以爬取简…...

趣讲TCP三次握手

一、TCP三次握手简介 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议。在TCP连接中&#xff0c;只有两方进行通信&#xff0c;它使用校验和、确认和重传机制来保证数据的可靠传输。…...