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

数据结构之红黑树的 “奥秘“

目录:

一.红黑树概念

二. 红黑树的性质

.红黑树的实现

四.红黑树验证 

五.AVL树和红黑树的比较

一.红黑树概念

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



二. 红黑树的性质:

1. 每个结点不是红色就是黑色

2. 根节点是黑色的

3. 如果一个节点是红色的,则它的两个孩子结点是黑色的【没有2个连续的红色节点】

4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点也就是(每条路径的黑色节点数相等)

5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

总结性质:最长路径最多是最短路径的2倍.

总结性质推导:



.红黑树的实现:

1.红黑树节点的定义 :

这里注意我们定义一个枚举来储存红黑树节点的颜色

public class RBTree {static class RBTreeNode {public RBTreeNode left;public RBTreeNode right;public RBTreeNode parent;public int val;public COLOR color;//枚举public RBTreeNode(int val) {this.val = val;//新创建的节点默认是红色this.color = COLOR.RED;}}public RBTreeNode root;
}

 

2.红黑树的插入:

这里我们要围绕红黑树上面的几条性质构建红黑树;但是红黑树是在二叉搜索树的基础上加上其平衡限制条件,所有我们构建时可以借鉴二叉搜索树方式。

步骤一:和二叉二叉搜索树一样找到要插入的节点;

步骤二:调整插入的节点让其满足红黑树的性质;

所有我们构建红黑树总共有三种情况

这里注意:插入节点默认为红色节点,推导如下:

3.构建红黑树的有三种情况:

3.1.情况一: cur为红,p为红,g为黑,u存在且为红:

图解:

代码:

 //开始调整颜色while (parent != null && parent.color == COLOR.RED) {RBTreeNode grandParent = parent.parent;/**情况一:** cur为红,p为红,g为黑,uncle存在且为红**  parent在grandParent左边,uncle在grandParent右边*/if (parent == grandParent.left) {RBTreeNode uncle = grandParent.right;if (uncle != null && uncle.color == COLOR.RED) {parent.color = COLOR.BLACK;uncle.color = COLOR.BLACK;grandParent.color = COLOR.RED;//预防grandParent的父亲为红色,就还有子树,继续向上修改cur = grandParent;parent = cur.parent;}

3.2.情况二: cur为红,p为红,g为黑,u不存在或者u为黑:

这里注意要先grandParent右旋,然后再调整颜色,parent改为 黑色,grandParent改为红色

 图解:

代码:

/** 情况二:
* cur为红,p为红,g为黑,uncle为黑色,或者uncle不存在
*
*  方法:
*  1.先右单旋
*  2.再改颜色*/
rotateRight(grandParent);
parent.color = COLOR.BLACK;
grandParent.color = COLOR.RED;

3.3.情况三: 调整过程中,cur为红,p为红,g为黑,u不存在/u为黑:

这里先左旋parent,再把parent 和 cur 的引用交换变为和情况二类似,再当作情况二处理(右旋改颜色,图片上笔误是右旋

代码:

/*** 情况三:
* 先左单旋parent
* 再交换parent和cur的引用,变成情况二处理
*/
if (parent.right == cur) {
rotateLeft(parent);
RBTreeNode tmp = parent;
parent = cur;
cur = tmp;}//变成情况二

 

当parent == grandParent.right,和上面三种情况完全相反,为镜相关系。

插入全部代码如下:

public class RBTree {static class RBTreeNode {public RBTreeNode left;public RBTreeNode right;public RBTreeNode parent;public int val;public COLOR color;//枚举public RBTreeNode(int val) {this.val = val;//新创建的节点默认是红色this.color = COLOR.RED;}}public RBTreeNode root;//插入:public boolean insert(int val) {RBTreeNode node = new RBTreeNode(val);if (root == null) {root = node;//插入节点默认为红色所有,当root为空时,要把插入的节点变为黑色root.color = COLOR.BLACK;return true;}RBTreeNode cur = root;RBTreeNode parent = null;while (cur != null) {if (cur.val < val) {parent = cur;cur = cur.right;} else if (cur.val > val) {parent = cur;cur = cur.left;} else {return false;}}if (parent.val < val) {parent.right = node;} else {parent.left = node;}node.parent = parent;cur = node;//指向新插入的节点//开始调整颜色while (parent != null && parent.color == COLOR.RED) {RBTreeNode grandParent = parent.parent;/**情况一:** cur为红,p为红,g为黑,uncle存在且为红**  parent在grandParent左边,uncle在grandParent右边*/if (parent == grandParent.left) {RBTreeNode uncle = grandParent.right;if (uncle != null && uncle.color == COLOR.RED) {parent.color = COLOR.BLACK;uncle.color = COLOR.BLACK;grandParent.color = COLOR.RED;//预防grandParent的父亲为红色,就还有子树,继续向上修改cur = grandParent;parent = cur.parent;} else {/*** 情况三:* 先左单旋parent* 再交换parent和cur的引用,变成情况二处理*/if (parent.right == cur) {rotateLeft(parent);RBTreeNode tmp = parent;parent = cur;cur = tmp;}//变成情况二/** 情况二:* cur为红,p为红,g为黑,uncle为黑色,或者uncle不存在**  方法:*  1.先右单旋*  2.再改颜色*/rotateRight(grandParent);parent.color = COLOR.BLACK;grandParent.color = COLOR.RED;}} else {//下面情况和上面情况完全相反//parent == grandParent.rightRBTreeNode uncle = grandParent.left;if (uncle != null && uncle.color == COLOR.RED) {parent.color = COLOR.BLACK;uncle.color = COLOR.BLACK;grandParent.color = COLOR.RED;//预防grandParent的父亲为红色,就还有子树,继续向上修改cur = grandParent;parent = cur.parent;} else {if (parent.left == cur) {rotateRight(parent);RBTreeNode tmp = parent;parent = cur;cur = tmp;}//变成情况二rotateLeft(grandParent);parent.color = COLOR.BLACK;grandParent.color = COLOR.RED;}}}//当parent为空时,要把根节点变为黑色root.color = COLOR.BLACK;return true;}/*** 右单旋* @param parent*/private void rotateRight (RBTreeNode parent){RBTreeNode subL = parent.left;RBTreeNode subRL = subL.right;parent.left = subRL;subL.right = parent;//如果旋转的整棵树也是一个子树,记录下原来该树的父亲,后续修改RBTreeNode pParent = parent.parent;if (subRL != null) {subRL.parent = parent;}parent.parent = subL;//看看整棵树是否也是一个子树if (parent == root) {root = subL;root.parent = null;} else {//是子树就确定这棵树是左子树还是右子树if (pParent.left == parent) {pParent.left = subL;} else {pParent.right = subL;}}subL.parent = pParent;}/*** 左单旋* @param parent*/private void rotateLeft (RBTreeNode parent){RBTreeNode subR = parent.right;RBTreeNode subRL = subR.left;parent.right = subRL;subR.left = parent;RBTreeNode pParent = parent.parent;if (subRL != null) {subRL.parent = parent;}parent.parent = subR;//看看整棵树是否也是一个子树if (parent == root) {root = subR;root.parent = null;} else {//是子树就确定这棵树是左子树还是右子树if (pParent.left == parent) {pParent.left = subR;} else {pParent.right = subR;}}subR.parent = pParent;}
}


四.红黑树验证:

1.红黑树的检测分为两步:

步骤一: 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

步骤二:检测其是否满足红黑树的性质 

 

步骤一: 检测其是否满足二叉搜索树(中序遍历是否为有序序列):

代码:

 /**1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)* 中序遍历:* @param root*/public void inorder(RBTreeNode root){if(root == null){return;}inorder(root.left);System.out.print(root.val+ " ");inorder(root.right);}

步骤二:检测其是否满足红黑树的性质 :

//2.检测其是否满足红黑树的性质:public boolean isRBTree(){if(root == null){//空树也是红黑树return true;}if(root.color != COLOR.BLACK){System.out.println("违反了性质2:根节点不是黑色");return false;}RBTreeNode cur = root;//blackNum是事先计算好一边黑色节点的个数int blackNum = 0;while (cur != null){if (cur.color == COLOR.BLACK){blackNum++;}cur = cur.left;}//判断性质三有没有两个红色的节点 && 判断性质四:每条路径的黑色节点个数是否相等return checkRedColor(root) && checkBlackNum(root,blackNum,0);}/*** 判断性质三有没有两个红色的节点:* 思路:遍历当前二叉树节点如果是红色,则判断他的父亲节点是不是红色* @param root* @return*/private boolean checkRedColor(RBTreeNode root){if(root == null){return true;}if (root.color == COLOR.RED){RBTreeNode parent = root.parent;if (parent != null && parent.color == COLOR.RED){System.out.println("违反了性质三: 连续出现两个红色的节点");return false;}}return checkRedColor(root.left) && checkRedColor(root.right);}/***判断性质四:每条路径的黑色节点个数是否相等* @param root* @param blackNum:事先计算好黑色节点的个数* @param pathBlackNum:每次递归计算的黑色节点的个数* 思路:看 blackNum 和 pathBlackNum 的数量是否相等* @return*/private boolean checkBlackNum(RBTreeNode root,int blackNum, int pathBlackNum){if(root == null){return true;}if (root.color == COLOR.BLACK){pathBlackNum++;}//blackNum 和 pathBlackNum 的数量是否相等就不满足性质if (root.left == null && root.right == null){if(pathBlackNum != blackNum){System.out.println("违反了性质四:每条路径的黑色节点个数不相等了!");return false;}}return checkBlackNum(root.left,blackNum,pathBlackNum)&& checkBlackNum(root.right,blackNum,pathBlackNum);}

  



五.AVL树和红黑树的比较

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

 

补充:java集合框架中的:TreeMap、TreeSet底层使用的就是红黑树

相关文章:

数据结构之红黑树的 “奥秘“

目录&#xff1a; 一.红黑树概念 二. 红黑树的性质 三.红黑树的实现 四.红黑树验证 五.AVL树和红黑树的比较 一.红黑树概念 1.红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何 一条从根…...

【鸿蒙 HarmonyOS NEXT】使用EventHub进行数据通信

✨本人自己开发的开源项目&#xff1a;土拨鼠充电系统 ✨踩坑不易&#xff0c;还希望各位大佬支持一下&#xff0c;在GitHub给我点个 Start ⭐⭐&#x1f44d;&#x1f44d; ✍GitHub开源项目地址&#x1f449;&#xff1a;https://github.com/cheinlu/groundhog-charging-syst…...

大模型RAG实战|构建知识库:文档和网页的加载、转换、索引与存储

我们要开发一个生产级的系统&#xff0c;还需要对LlamaIndex的各个组件和技术进行深度的理解、运用和调优。本系列将会聚焦在如何让系统实用上&#xff0c;包括&#xff1a;知识库的管理&#xff0c;检索和查询效果的提升&#xff0c;使用本地化部署的模型等主题。我将会讲解相…...

江协科技stm32————11-5 硬件SPI读写W25Q64

一、开启时钟&#xff0c;开启SPI和GPIO的时钟 二、初始化GPIO口&#xff0c;其中SCK和MOSI是由硬件外设控制的输出信号&#xff0c;配置为复用推挽输出 MISO是硬件外设的输入信号&#xff0c;配置为上拉输入&#xff0c;SS是软件控制的输出信号&#xff0c;配置为通用推挽输出…...

网络编程day04(UDP、Linux IO 模型)

目录 【1】UDP 1》通信流程 2》函数接口 1> recvfrom 2> sendto 3》代码展示 1> 服务器代码 2> 客户端代码 【2】Linux IO 模型 场景假设一 1》阻塞式IO&#xff1a;最常见、效率低、不耗费CPU 2》 非阻塞 IO&#xff1a;轮询、耗费CPU&#xff0c;可以处…...

【android10】【binder】【2.servicemanager启动——全源码分析】

系列文章目录 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录 …...

Java实现简易计算器功能(idea)

目的&#xff1a;写一个计算器&#xff0c;要求实现加减乘除功能&#xff0c;并且能够循环接收新的数据&#xff0c;通过用户交互实现。 思路&#xff1a; &#xff08;1&#xff09;写4个方法&#xff1a;加减乘除 &#xff08;2&#xff09;利用循环switch进行用户交互 &…...

Parsec问题解决方案

Parsec目前就是被墙了&#xff0c;有解决方案但治标不治本&#xff0c;如果想稳定串流建议是更换稳定的串流软件&#xff0c;以下是一些解决方案 方案一&#xff1a;在%appdata%/Parsec/config.txt中&#xff0c;添加代理 app_proxy_address 127.0.0.1 app_proxy_scheme http…...

Swift 创建扩展(Extension)

类别(Category) 和 扩展(Extension) 的 用法很多. 常用的 扩展(Extension) 有分离代码和封装模块的功能,例如登陆页面有注册功能,有登陆功能,有找回密码功能,都写在一个页面就太冗余了,可以考虑使用 扩展(Extension) 登陆页面的方法来分离代码 本文介绍Swift 如何创建扩展(Ex…...

九月五日(k8s配置)

一、安装环境 环境准备&#xff1a;&#xff08;有阿里云&#xff09; k8s-master 192.168.1.11 k8s-node1 192.168.1.22 k8s-node2 192.168.1.33 二、前期准备 在k8s-master主机 [rootk8s-master ~]# vim /etc/hosts …...

某极验4.0 -消消乐验证

⚠️前言⚠️ 本文仅用于学术交流。 学习探讨逆向知识&#xff0c;欢迎私信共享学习心得。 如有侵权&#xff0c;联系博主删除。 请勿商用&#xff0c;否则后果自负。 网址 aHR0cHM6Ly93d3cyLmdlZXRlc3QuY29tL2FkYXB0aXZlLWNhcHRjaGE 1. 浅聊一下 验证码样式 验证成功 - …...

洛谷 P10798 「CZOI-R1」消除威胁

题目来源于&#xff1a;洛谷 题目本质&#xff1a;贪心&#xff0c;st表&#xff0c;单调栈 解题思路&#xff1a;由于昨天联练习了平衡树&#xff0c;我就用平衡树STL打了个暴力&#xff0c;超时得了30分 这是暴力代码&#xff1a; #include<bits/stdc.h> using name…...

Pow(x, n)

题目 实现 pow(x, n) &#xff0c;即计算 x 的 n 次幂函数&#xff08;即&#xff0c;xn&#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000示例 2&#xff1a; 输入&#xff1a;x 2.10000, n 3 输出&#xff1a;9.26100示…...

一文带你学会使用滑动窗口

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;leetcode刷题 ​ ​ 209.长度最小的子数组 求最短长度之和等于目标值。 方法一&#xff1a; 暴力枚举&#xff08;会超时&#xff09; 从头开始遍历直到之和等于target然后更新结果。这…...

如何从0到1本地搭建whisper语音识别模型

文章目录 环境准备1. 系统要求2. 安装依赖项1:安装 Python 和虚拟环境2:安装 Whisper3:下载 Whisper 模型4:进行语音识别5:提高效率和精度6:开发和集成Whisper 是 OpenAI 发布的一个强大的语音识别模型,它可以将语音转换为文本,支持多语言输入,并且可以处理各种音频类…...

PyTorch 创建数据集

图片数据和标签数据准备 1.本文所用图片数据在同级文件夹中 ,文件路径为train/’ 2.标签数据在同级文件&#xff0c;文件路径为train.csv 3。将标签数据提取 train_csvpd.read_csv(train.csv)创建继承类 第一步&#xff0c;首先创建数据类对象 此时可以想象为单个数据单元的…...

[Java]SpringBoot登录认证流程详解

登录认证 登录接口 1.查看原型 2.查看接口 3.思路分析 登录核心就是根据用户名和密码查询用户信息,存在则登录成功, 不存在则登录失败 4.Controller Slf4j RestController public class LoginController {Autowiredprivate EmpService empService;/*** 登录的方法** param …...

【Day08】

目录 MySQL-多表查询-概述 MySQL-多表查询-内连接 MySQL-多表查询-外连接 MySQL-多表查询-[标量、列]子查询 MySQL-多表查询-[行、表]子查询 MySQL-多表查询-案例 MySQL-事务-介绍与操作 MySQL-事务-四大特性 MySQL-索引-介绍 MySQL-索引-结构 MySQL-索引-操作语法 …...

mongodb在Java中条件分组聚合查询并且分页(时间戳,按日期分组,年月日...)

废话不多说&#xff0c;先看效果图&#xff1a; SQL查询结果示例&#xff1a; 多种查询结果示例&#xff1a; 原SQL&#xff1a; db.getCollection("hbdd_order").aggregate([{// 把时间戳格式化$addFields: {orderDate: {"$dateToString": {"for…...

怎么样处理浮毛快捷又高效?霍尼韦尔、希喂、米家宠物空气净化器实测对比

掉毛多&#xff1f;掉毛快&#xff1f;猫毛满天飞对身体有危害吗&#xff1f;多猫家庭经验分享篇&#xff1a; 一个很有趣的现象&#xff0c;很多人在养猫、养狗后耐心都变得更好了。养狗每天得遛&#xff0c;养猫出门前得除毛&#xff0c;日复一日的重复磨练了极好的耐心。我家…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

Python实现简单音频数据压缩与解压算法

Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中&#xff0c;压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言&#xff0c;提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...

数据库正常,但后端收不到数据原因及解决

从代码和日志来看&#xff0c;后端SQL查询确实返回了数据&#xff0c;但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离&#xff0c;并且ai辅助开发的时候&#xff0c;很容易出现前后端变量名不一致情况&#xff0c;还不报错&#xff0c;只是单…...

2.2.2 ASPICE的需求分析

ASPICE的需求分析是汽车软件开发过程中至关重要的一环&#xff0c;它涉及到对需求进行详细分析、验证和确认&#xff0c;以确保软件产品能够满足客户和用户的需求。在ASPICE中&#xff0c;需求分析的关键步骤包括&#xff1a; 需求细化&#xff1a;将从需求收集阶段获得的高层需…...

【阅读笔记】MemOS: 大语言模型内存增强生成操作系统

核心速览 研究背景 ​​研究问题​​&#xff1a;这篇文章要解决的问题是当前大型语言模型&#xff08;LLMs&#xff09;在处理内存方面的局限性。LLMs虽然在语言感知和生成方面表现出色&#xff0c;但缺乏统一的、结构化的内存架构。现有的方法如检索增强生成&#xff08;RA…...

无需布线的革命:电力载波技术赋能楼宇自控系统-亚川科技

无需布线的革命&#xff1a;电力载波技术赋能楼宇自控系统 在楼宇自动化领域&#xff0c;传统控制系统依赖复杂的专用通信线路&#xff0c;不仅施工成本高昂&#xff0c;后期维护和扩展也极为不便。电力载波技术&#xff08;PLC&#xff09;的突破性应用&#xff0c;彻底改变了…...

比特币:固若金汤的数字堡垒与它的四道防线

第一道防线&#xff1a;机密信函——无法破解的哈希加密 将每一笔比特币交易比作一封在堡垒内部传递的机密信函。 解释“哈希”&#xff08;Hashing&#xff09;就是一种军事级的加密术&#xff08;SHA-256&#xff09;&#xff0c;能将信函内容&#xff08;交易细节&#xf…...

标注工具核心架构分析——主窗口的图像显示

&#x1f3d7;️ 标注工具核心架构分析 &#x1f4cb; 系统概述 主要有两个核心类&#xff0c;采用经典的 Scene-View 架构模式&#xff1a; &#x1f3af; 核心类结构 1. AnnotationScene (QGraphicsScene子类) 主要负责标注场景的管理和交互 &#x1f527; 关键函数&…...