数据结构系列之红黑树


前言

红黑树是比较重要的一颗树了,map和set的底层就是红黑树,一定要牢牢记住。


一、什么是红黑树

首先:红黑树仍然是一颗搜索二叉树,但他引入了颜色这一概念,每个结点多一个存储位来存储颜色,它通过维护下面五条规则来保证,最长路径不超过最短路径的二倍。

1.每个结点不是黑颜色就是红颜色
2.根节点是黑色
3.如果一个节点是红色,则他存在的孩子节点是黑色,换句话说,任意一条路径不存在连续的红色节点。
4.对于每个节点,到所有NIL节点的路径上,均含有相同数量的黑色节点。
5.每个NIL节点都是黑色的
为什么设计第五条规则?看图:
在这里插入图片描述
这五条规则里最重要的就是三和四,插入也要维护三和四。

为什么维护了这五条规则就能达到最长路径不超过最短路径的二倍呢?
最短路径:全是黑色节点
最长路径:一黑一红,又因为黑色节点的数量相同,所以最长的路径最多是最短路径的二倍。

二、模拟

红黑树还是模拟插入过程,还是写成K,V和三叉链结构

节点定义

enum class Color {RED,BLACK
};template<class K,class V>
struct RBTreeNode {RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Color color;pair<K, V> _kv;RBTreeNode(const pair<K,V>& p):_left(nullptr),_right(nullptr),_parent(nullptr),color(Color::RED),_kv(p){}
};

这里有两个注意的点:
1.这个枚举是C++新搞出来的,和原来枚举不同的就是不是全局的。
2.节点的默认颜色搞成红色,如果搞成黑色,这一条路径上的黑色节点加一,而其他所有路径上的黑色数目就会相对较少,不好维护,所以违反第三条规则,维护第三条规则,这需要维护这一条和叔叔路径上的。

搜索树的逻辑插入:

bool insert(const pair<K, V>& p)
{if (_root == nullptr){_root = new Node(p);_root->color = Color::BLACK;return true;}else{Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first < p.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > p.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(p);if (parent->_kv.first < p.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//这之前都是搜索树的逻辑//

调整颜色的逻辑:
在这里插入图片描述

	while (parent && parent->color == Color::RED){Node* grandf = parent->_parent;if (grandf->_left == parent){Node* uncle = grandf->_right;//叔叔存在且为红if (uncle && uncle->color == Color::RED){//判断uncle->color = parent->color = Color::BLACK;grandf->color = Color::RED;//继续判断cur = grandf;parent = cur->_parent;}//叔叔不存在或者为黑else{//开旋if (parent->_left == cur){RotateRight(grandf);parent->color = Color::BLACK;grandf->color = Color::RED;}else{//parent->right  是cur,grandf的左边是parentRotateLeft(parent);RotateRight(grandf);cur->color = Color::BLACK;grandf->color = Color::RED;}}}//祖父的右边是父亲else{Node* uncle = grandf->_left;//uncle不存在 或者uncle为黑//uncle为红if (uncle && uncle->color == Color::RED){parent->color = uncle->color = Color::BLACK;grandf->color = Color::RED;cur = grandf;parent = cur->_parent;}//uncle为黑或者不存在else{//旋转//   grandf//uncle                parent//curif (cur == parent->_right){RotateLeft(grandf);parent->color = Color::BLACK;grandf->color = Color::RED;}else{RotateRight(parent);RotateLeft(grandf);cur->color = Color::BLACK;grandf->color = Color::RED;}}}}_root->color = Color::BLACK;return true;
}

其实还可以,顺一顺思路:
当父亲存在并且父亲的颜色是红就更新
先判断祖父的左边是父亲还是右边,需要根据这个旋转
然后判断uncle,也就是祖父的另一个孩子,如果uncle为红,和parent一起改颜色,往上更新。
如果为黑或者不存在,要旋转,直线型单旋,折线型双旋,注意控制颜色,旋转完的父亲一定为黑,左右两个孩子都为红。

三、验证是否正确

1.验证是否为二叉搜索树,走一个中序遍历就可以,有序即为二叉搜索树。

2.验证是否红黑树满足条件:
主要是看三和四,三:看有无连续红色节点,利用父亲来判断,
四:看黑色节点的数量,可以先求出一条路径的数量,然后再去递归所有路径进行比较。

bool CheckColor(Node* node,int blacknum,int basic)
{
if (node == nullptr)
{if (blacknum != basic){cout << "违背了第四条规则" << endl;return false;}return true;
}
if (node->color == Color::BLACK)
{blacknum++;
}
//检查孩子很麻烦,检查父亲
if (node->color == Color::RED && node->_parent && node->_parent->color == Color::RED)
{cout << "出现了连续的红色结点" << endl;return false;
}
return CheckColor(node->_left,blacknum,basic) && CheckColor(node->_right, blacknum, basic);}
bool _IsBalance(Node* node)
{if (node == nullptr) return true;if (node->color != Color::BLACK){//根节点是黑色return false;//第二条}int basic = 0;Node* cur = node;while (cur){if (cur->color == Color::BLACK){basic++;}cur = cur->_left;}return CheckColor(node,0,basic);
}

四、性能

代码测试

int main()
{RBTree<int, int> rb;AVLTree<int, int> avl;srand(time(0));size_t N = 100000000;vector<int> v(N);for (size_t i = 0; i < N; ++i) v[i] = rand() + i;int begin1 = clock();for (size_t i = 0; i < N; ++i) {rb.insert({ v[i],v[i] });}int end1 = clock();int begin2 = clock();for (size_t i = 0; i < N; ++i){avl.insert({ v[i],v[i] });}int end2 = clock();if (rb.IsBalance() && avl.IsBalance()){cout << "插入" << N << "个数据" << "红黑树用时间" << end1 - begin1 << ' ' << "旋转次数:" << rb.RotateCount;cout << endl;cout << "插入" << N << "个数据" << "AVL树用时间" << end2 - begin2 << ' ' << "旋转次数:" << avl.Rotatecount;}return 0;
}

在这里插入图片描述

还可以去测试一下他们的高度,有序数据,随机数据,查找的效率等等。
总体来说,红黑树是更优秀的,数据量越大红黑树的优势就越明显。

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


五、完整代码

个人gitee

总结

红黑树很重要!!!!一定要牢牢掌握。
接下来map和set还需要使用它。

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

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

相关文章

在OpenMP中,#pragma omp的使用

在OpenMP中&#xff0c;#pragma omp for 和 #pragma omp parallel for&#xff08;或 #pragma omp parallel num_threads(N)&#xff09;有本质区别&#xff0c;主要体现在 并行区域的创建 和 工作分配方式 上。以下是详细对比&#xff1a;1. #pragma omp for 作用 仅分配循环迭…

停止“玩具式”试探:深入拆解ChatGPT Agent的技术栈与实战避坑指南

摘要&#xff1a; 当许多人还在用ChatGPT写周报、生成样板代码时&#xff0c;其底层的Agent化能力已经预示着一场深刻的开发范式变革。这不再是简单的“AI辅助”&#xff0c;而是“人机协同”的雏形。本文旨在穿透表面的功能宣传&#xff0c;从技术栈层面拆解Agent模式的实现基…

element-plus安装以及使用

element-plus时为vue.js 3开发的组件库。 在引入前需要做如下准备 安装node.js https://blog.csdn.net/zlpzlpzyd/article/details/147704723 安装vue的脚手架vue-cli https://blog.csdn.net/zlpzlpzyd/article/details/149647351 安装element-plus github地址 https://git…

学习随想录-- web3学习入门计划

#60 转方向 web3 golang 以太坊应用 这是课表部分&#xff08;Golang以太坊方向&#xff09; Sheet b站up学习计划 第一阶段&#xff1a;基础能力构建&#xff08;1-2 个月&#xff09; 学习目标 掌握 Golang 核心语法与以太坊底层基础概念&#xff0c;建立开发知识框架。…

【RAG优化】PDF复杂表格解析问题分析

在构建检索增强生成(RAG)应用时,PDF文档无疑是最重要、也最普遍的知识来源之一。然而,PDF中潜藏着RAG系统的难点问题——复杂表格。这些表格富含高密度的结构化信息,对回答精准问题至关重要,但其复杂的视觉布局(多层表头、合并单元格、跨页表格等)常常让标准的文本提取…

ReAct Agent(LangGraph实现)

文章目录参考资料一 AI Agent二 ReAct三 LangGraph实现ReAct代理3.1 SerperAPI实时联网搜索3.2 ReAct实现参考资料 entic RAG 架构的基本原理与应用入门 一 AI Agent AI Agent 整个过程是一个动态循环。Agent不断从环境中学习&#xff0c;通过其行动影响环境&#xff0c;然后…

如何从0到1的建立组织级项目管理体系【现状诊断】

今天我想给大家分享是“如何在企业中从0到1的去建立PMO的组织级项目管理体系。”的系列文章&#xff0c;这是我近几年来一直在努力的尝试去探索和实践的过程&#xff0c;从0到1的过程。当我最开始去接手这样一个场景的时候所需要做的第一件事情是诊断和差距分析。这是多年以来做…

网络通信协议详解:TCP协议 vs HTTP协议

在计算机网络中&#xff0c;TCP&#xff08;传输控制协议&#xff09;和HTTP&#xff08;超文本传输协议&#xff09;是两个核心协议&#xff0c;但它们的职责和层级完全不同。TCP是底层传输协议&#xff0c;负责数据的可靠传输&#xff1b;HTTP是应用层协议&#xff0c;定义了…

[Qt]QString隐式拷贝

引言在Qt框架中&#xff0c;QString 作为字符串处理的核心类&#xff0c;其高效的内存管理机制一直是开发者津津乐道的特性。这背后的关键便是 隐式共享&#xff08;Implicit Sharing&#xff09;&#xff0c;也称为 写时复制&#xff08;Copy-On-Write, COW&#xff09;。本文…

命令行创建 UV 环境及本地化实战演示—— 基于《Python 多版本与开发环境治理架构设计》的最佳实践

命令行创建 UV 环境及本地化实战&#xff1a;基于架构设计的最佳实践 Python 多版本环境治理理念驱动的系统架构设计&#xff1a;三维治理、四级隔离、五项自治 原则-CSDN博客 使用 Conda 工具链创建 UV 本地虚拟环境全记录——基于《Python 多版本与开发环境治理架构设计》-CS…

跨域问题全解:从原理到实战

在计算机网络中&#xff0c;跨域&#xff08;Cross-Origin&#xff09; 指的是浏览器出于安全考虑&#xff0c;限制网页脚本&#xff08;如 JavaScript&#xff09;向与当前页面不同源&#xff08;Origin&#xff09; 的服务器发起请求的行为。这是由浏览器的同源策略&#xff…

(46)elasticsearch-华为云CCE无状态负载部署

一、准备好elasticsearch镜像并提前上传到镜像仓库 此次准备的是elasticsearch:v7.10.2 二、开始部署 负载名称:es-deployment 注意:内部配额太低会造成多次重启 环境变量: #单节点启动(实例pod可以多增加几个) discovery.type single-node 三、添加svc 四、注意:…

HCLP--MGER综合实验

一、拓扑图二、需求1、R5为ISP&#xff0c;只能进行IP地址配置&#xff0c;其所有地址均配为公有I地址; 2、R1和R5间使用PPP的PAP认证&#xff0c;R5为主认证方&#xff0c; R2与R5之间使用ppp的CHAP认证&#xff0c;R5为主认证方; R3与R5之间使用HDLc封装; 3、R1、R2、R3构建一…

idea中无法删除模块,只能remove?

1.先对module右键想要删除的module&#xff0c;选择remove module&#xff08;这是idea为了避免误操作&#xff09; 2.在remove module后&#xff0c;模块并未从项目结构中删除&#xff08;磁盘中也依旧存在&#xff09;&#xff0c;但再次右击你会发现&#xff0c;出现了del…

青藤天睿RASP再次发威!捕获E签宝RCE 0day漏洞

在2025年HVV关键攻防节点上&#xff0c;攻击队对E签宝电子合同服务发起的0day攻击被青藤天睿RASP截获。该漏洞可使攻击者在未授权情况下实现服务器远程代码执行&#xff08;RCE&#xff09;&#xff0c;进而控制服务器&#xff0c;构成横向渗透的关键跳板。>>>>漏洞…

Lua(字符串)

Lua字符串基础Lua中的字符串是不可变序列&#xff0c;可以包含任意字节数据&#xff08;包括嵌入的\0&#xff09;。字符串可以用单引号、双引号或长括号&#xff08;[[ ]]&#xff09;定义&#xff1a;str1 "Hello" str2 World str3 [[Multi-line string]]字符串…

大模型蒸馏(distillation)---从DeepseekR1-1.5B到Qwen-2.5-1.5B蒸馏

目录 1.1 蒸馏目标 2 环境准备 2.1依赖库安装 2.2 硬件要求 2.3 模型与数据集下载 2.3.1 教师模型下载 2.3.2 学生模型下载 2.3.3 数据集准备或下载 3.过程日志 4. 模型加载与配置 4.1 加载教师模型 4.2 加载学生模型 4.3 数据预处理函数 4.4 数据收集器 4.5 定义…

通过redis_exporter监控redis cluster

环境说明&#xff1a; 现在有一套redis cluster&#xff0c;部署是3主机6实例架构部署。需要采集对应的指标&#xff0c;满足异常监控告警&#xff0c;性能分析所需。 环境准备 以下环境需要提前部署完成。 redis cluser prometheus alertmanager grafna redis_exporter部署 我…

第二十天(正则表达式与功能实际运用)

在程序员一生的工作中&#xff0c;遇到的最多的数据就是字符串字符串里面很有可能有很多的不需要的信息我们需要从中间挑选出我们需要的如果循环去写&#xff0c;比较简单的时候问题不大规则多了&#xff0c;你的工作量会成倍上升的为了解决这个问题 ---- 正则表达式正则表达式…

0基础法考随手笔记 03(刑诉05 刑事证据与证明+06 强制措施)

1.如何区分书证和电子数据 书面材料是否为书证&#xff1f;→ 看内容是否直接源于案件事实&#xff08;不是 “记录别人陈述” 的载体&#xff09;。 证据清单是否为证据&#xff1f;→ 看谁做的清单&#xff08;侦查人员做的勘查笔录是证据&#xff0c;当事人做的目录不是&…