C++色彩博弈的史诗:红黑树

文章目录

  • 1.红黑树的概念
  • 2.红黑树的结构
  • 3.红黑树的插入
  • 4.红黑树的删除
  • 5.红黑树与AVL树的比较
  • 6.红黑树的验证
  • 希望读者们多多三连支持
  • 小编会继续更新
  • 你们的鼓励就是我前进的动力!

红黑树是一种自平衡二叉查找树,每个节点都带有颜色属性,颜色或为红色或为黑色,可以理解为 AVL 树的进阶版,建议系统学习完 AVL 树再来看本篇博客

传送门:C++漫步结构与平衡的殿堂:AVL树

1.红黑树的概念

在这里插入图片描述

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是 RedBlack。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保最长路径的节点数量不超过最短路径节点数量的两倍(刚好两倍是可以的),因而是接近平衡的

一个合格的红黑树需要满足以下条件:

  • 每个结点不是红色就是黑色
  • 根节点是黑色的
  • 如果一个节点是红色的,则它的两个孩子结点必须是黑色的,任何路径都没有连续的红色节点,也就是说可以有连续的黑色节点,但不可能一颗红黑树全是黑色节点
  • 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色节点
  • 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

最短路径就是仅由黑色节点构成的路径。因为如果路径中插入红色节点,会使路径变长,而全黑路径不包含额外红色节点,所以是最短的

最长路径是红黑交替出现的路径。即每一个黑色节点后面都跟着一个红色节点(但红色节点后不能再有红色节点)

设最短路径的黑色节点数量为 n ,由于所有路径黑色节点数量相同,最长路径的黑色节点数量也为 n ,那么最长路径由于红黑交替的节点总数最多为 2n 。所以,最长路径的节点个数不会超过最短路径节点个数的两倍

2.红黑树的结构

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;RBTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_col(RED){}
};

在节点的定义中,为什么要将节点的默认颜色给成红色的?

红黑树的性质要求从根节点到每个叶子节点的路径上黑色节点数量相同。将新节点设为红色,在插入过程中,如果其父节点是黑色,那么插入红色节点不会影响任何路径上黑色节点的数量,也就不需要对树进行调整来满足红黑树的性质,从而减少了调整的可能性,提高了插入操作的效率

如果新节点是黑色,那么插入后可能会导致某个路径上的黑色节点数量增加,这会引发更复杂的 “双黑” 问题,即删除或插入操作后出现一个节点需要同时承担两个黑色节点的情况,处理起来相对复杂。而默认新节点为红色,出现的问题主要是红节点冲突,处理相对简单,以下的插入会详细解释原因

3.红黑树的插入

typedef RBTreeNode<K, V> Node;
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);cur->_col = RED;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 (parent == grandfather->_left){Node* uncle = grandfather->_right;// u存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // u不存在 或 存在且为黑{if (cur == parent->_left){//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//   p//		cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else // parent == grandfather->_right{Node* uncle = grandfather->_left;// u存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){// g//	  p//       cRotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{// g//	  p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}

对于插入的节点,可能会遇到三种情况:

🚩uncle存在且为红
在这里插入图片描述

我们定义插入节点为 cur,其父节点为 parent,父节点的兄弟节点为 uncle,父节点的父节点为 grandfather

当新插入节点的双亲节点颜色为红色时,就违反了不能有连在一起的红色节点,想要尽可能不破坏红黑树的平衡结构的情况下正常插入,那么通过变色解决是最好的

请添加图片描述

不能连续出现红色节点,还要保持每条路径的黑色节点相同,可以将 parentuncle 变黑,grandfather 变红解决

在这里插入图片描述

发现处理完之后,在子树上是保持平衡的,但是 grandfather 又出现了连续红色节点,这是其中一种情况,总共有三种情况:

  1. grandfather 没有父亲,就是根,直接变黑就好了
  2. grandfather 有父亲,父亲是黑色,直接结束
  3. grandfather 有父亲,父亲是红色,重复上述操作

很明显示例就是第三种

🚩uncle不存在

在这里插入图片描述

uncle 不存在的时候,发现通过变色已经不能解决问题了,这个时候就要旋转调整结构了,根据 cur 的位置判断进行单旋还是双旋

在这里插入图片描述

然后根据结构性质进行变色即可

🚩uncle存在且为黑
在这里插入图片描述

uncle 存在且为黑的时候,情况和 uncle 不存在是一样的

🔥值得注意的是: AVL 树旋转可以根据平衡因子为 2 的相对位置来判断是要单旋还是双旋,红黑树根据 grandfatherparentcur 的相对位置来判断,也就是要多画图

4.红黑树的删除

红黑树的删除本节不做讲解,有兴趣可参考:《算法导论》或者《STL源码剖析》

传送门:博客园相关讲解

5.红黑树与AVL树的比较

可是红黑树的时间复杂度比AVL树更高啊,为什么反而用的更多?

红黑树AVL树
最长路径不超过最短路径的2倍高度差不超过1
10亿个值10亿个值
2*logN->60logN->30

可以看到数据,性能处理上大概相差两倍,但是要知道 CPU 的性能是很强大的,每秒能处理十几亿的数据,这点差距根本不足为惧,而且红黑树和 AVL 树是处于同一量级的,但是 AVL 树的插入删除需要大量的旋转,控制严格平衡的代价太大,因此使用红黑树更多

6.红黑树的验证

🚩检查是否有连续红色节点

bool CheckColour(Node* root, int blacknum, int benchmark)
{if (root == nullptr){if (blacknum != benchmark)return false;return true;}if (root->_col == BLACK){++blacknum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_kv.first << "出现连续红色节点" << endl;return false;}return CheckColour(root->_left, blacknum, benchmark)&& CheckColour(root->_right, blacknum, benchmark);
}

🚩检查是否平衡

bool IsBalance()
{return IsBalance(_root);
}bool IsBalance(Node* root)
{if (root == nullptr)return true;if (root->_col != BLACK){return false;}// 基准值int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}return CheckColour(root, 0, benchmark);
}int Height()
{return Height(_root);
}int Height(Node* root)
{if (root == nullptr)return 0;int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

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

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

相关文章

基于STM32、HAL库的CH342F USB转UART收发器 驱动程序设计

一、简介: CH342F是一款USB转串口芯片,由南京沁恒电子(WCH)生产,具有以下特点: 支持USB转UART、IrDA红外或SPI接口 内置时钟,无需外部晶振 支持5V和3.3V电源电压 最高支持3Mbps波特率 支持常用的MODEM联络信号 内置EEPROM,可配置设备VID/PID/序列号等 二、硬件接口: C…

项目功能-图片清理(上)

一、图片存储介绍 在实际开发中&#xff0c;我们会有很多处理不同功能的服务器。例如&#xff1a; 应用服务器&#xff1a;负责部署我们的应用 数据库服务器&#xff1a;运行我们的数据库 文件服务器&#xff1a;负责存储用户上传文件的服务器 分服务器处理的目的是让服务…

创建三个网络,分别使用RIP、OSPF、静态,并每个网络10个电脑。使用DHCP分配IP

DHCP 自动分配IP&#xff0c;集中管理&#xff0c;提高效率 在路由器中设置 Router>en Router#conf t Router(config)#ip dhcp pool ip30 //创建DHCP地址池 Router(dhcp-config)#network 192.168.30.0 255.255.255.0 // 配置网络地址和子网掩码 Router(dhcp-config)#defa…

如何使用 WMIC 命令在 Windows 11 或 10 上卸载软件

如果您正在寻找一个命令提示符或 PowerShell 命令来卸载 Windows 应用程序,那么使用 wmic(Windows Management Instrumentation Command-line)是一种强大的技术,尤其是在处理难以卸载的程序或自动化卸载过程时。在本教程中,我们将学习如何使用 wmic 来卸载软件。 先决条件…

FEKO许可证的安全与合规性

在电磁仿真领域&#xff0c;FEKO软件因其出类拔萃的性能和广泛的应用场景&#xff0c;赢得了全球用户的广泛赞誉。但在这背后&#xff0c;是什么让FEKO在众多竞争者中脱颖而出&#xff1f;答案是其许可证的安全与合规性。它们不仅为用户提供了坚固的保障&#xff0c;更确保了用…

ESP32开发入门(九):HTTP服务器开发实践

一、HTTP服务器基础 1.1 什么是HTTP服务器&#xff1f; HTTP服务器是能够处理HTTP请求并返回响应的网络服务程序。在物联网应用中&#xff0c;ESP32可以作为轻量级HTTP服务器&#xff0c;直接接收来自客户端(如浏览器、手机APP)的请求。 1.2 ESP32作为HTTP服务器的特点 轻量…

《棒球百科》MLB棒球公益课·棒球1号位

MLB&#xff08;美国职业棒球大联盟&#xff09;的棒球公益课通过推广棒球运动、普及体育教育&#xff0c;对全球多个地区产生了多层次的影响&#xff1a; 1. 体育文化推广 非传统棒球地区的普及&#xff1a;在棒球基础较弱的地区&#xff08;如中国、欧洲部分国家&#xff09…

Baumer工业相机堡盟工业相机的工业视觉是否可以在室外可以做视觉检测项目

Baumer工业相机堡盟工业相机的工业视觉是否可以在室外可以做视觉检测项目 Baumer工业相机​视觉检测项目为什么偏爱“室内环境”&#xff1f;​工业视觉中为什么倾向于室内环境**保障人员与设备安全**&#xff1a;室内环境可以提供更好的安全保障&#xff0c;避免检测设备和人员…

1. 使用 IntelliJ IDEA 创建 React 项目:创建 React 项目界面详解;配置 Yarn 为包管理器

1. 使用 IntelliJ IDEA 创建 React 项目&#xff1a;创建 React 项目界面详解&#xff1b;配置 Yarn 为包管理器 &#x1f9e9; 使用 IntelliJ IDEA 创建 React 项目&#xff08;附 Yarn 配置与 Vite 建议&#xff09;&#x1f4f7; 创建 React 项目界面详解1️⃣ Name&#xf…

C++GO语言微服务之用户信息处理②

目录 01 03-获取用户信息-上 02 04-获取用户信息-下 03 05-更新用户名实现 01 06-中间件简介和中间件类型 02 07-中间件测试和模型分析 03 08-中间件测试案例和小结 04 09-项目使用中间件 01 03-获取用户信息-上 ## Cookie操作 ### 设置Cookie go func (c *Context) …

QMK键盘固件开发全解析:QMK 固件开发的最新架构和规范(2025最新版)

QMK键盘固件开发全解析:QMK 固件开发的最新架构和规范(2025最新版) 📚 前言概述 QMK(Quantum Mechanical Keyboard)作为目前开源键盘固件领域的"扛把子",凭借其强大的功能和活跃的社区支持,已经成为众多DIY键盘爱好者的首选开发框架。无论是入门级玩家还是资…

极狐GitLab 容器镜像仓库功能介绍

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 极狐GitLab 容器镜像库 (BASIC ALL) 您可以使用集成的容器镜像库&#xff0c;来存储每个极狐GitLab 项目的容器镜像。 要为您…

Umi+React+Xrender+Hsf项目开发总结

一、菜单路由配置 1.umirc.ts 中的路由配置 .umirc.ts 文件是 UmiJS 框架中的一个配置文件&#xff0c;用于配置应用的全局设置&#xff0c;包括但不限于路由、插件、样式等。 import { defineConfig } from umi; import config from ./def/config;export default defineCon…

【运维】基于Python打造分布式系统日志聚合与分析利器

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在分布式系统中,日志数据分散在多个节点,管理和分析变得复杂。本文详细介绍如何基于Python开发一个日志聚合与分析工具,结合Logstash和F…

Python实战:海量获取京东商品信息

在数据驱动的商业时代&#xff0c;数据就是最宝贵的资源。对于电商从业者、市场分析师而言&#xff0c;从京东这类大型电商平台获取商品信息&#xff0c;能够为市场调研、竞品分析、销售策略制定提供重要依据。今天&#xff0c;就来分享如何用Python实现京东商品信息的海量获取…

聊一聊常见的超时问题:timeout

大家好&#xff0c;我是G探险者&#xff01; 在日常开发中&#xff0c;“超时&#xff08;Timeout&#xff09;”类错误是开发者们经常遇到的问题。无论是调用第三方服务、访问数据库&#xff0c;还是并发任务处理&#xff0c;都可能因超时而导致请求失败或系统异常。 本文将系…

创建型模式:工厂方法(Factory Method)模式

一、简介 工厂方法(Factory Method)模式是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。在 C# 中,工厂方法模式提供了一种更灵活的对象创建方式,将对象的创建和使用分离,提高了代码的可维护性和…

外网访问内网海康威视监控视频的方案:WebRTC + Coturn 搭建

外网访问内网海康威视监控视频的方案&#xff1a;WebRTC Coturn 需求背景 在仓库中有海康威视的监控摄像头&#xff0c;内网中是可以直接访问到监控摄像的画面&#xff0c;由于项目的需求&#xff0c;需要在外网中也能看到监控画面。 实现这个功能的意义在于远程操控设备的…

Redis 8.0正式发布,再次开源为哪般?

Redis 8.0 已经于 2025 年 5 月 1 日正式发布&#xff0c;除了一些新功能和性能改进之外&#xff0c;一个非常重要的改变就是新增了开源的 AGPLv3 协议支持&#xff0c;再次回归开源社区。 为什么说再次呢&#xff1f;这个需要从 2024 年 3 月份 Redis 7.4 说起&#xff0c;因为…

382_C++_在用户会话结束时,检查是否有其他会话仍然来自同一个客户端 IP 地址,没有连接状态设置为断开,否则为连接

之前出现的问题:重启管理机,工作机上面热备连接状态显示未连接 (此时是有一个工作机连接管理机的),所以正常应该是连接状态解决:根因分析: 重启管理机后,管理机给过来的cookie是空的,导致工作机同时存在两个管理机的session,在其中一个超时后,调用回调函数通知会话断开…