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

数据结构与算法——19.红黑树

这篇文章我们来讲一下红黑树。

目录

1.概述

1.1红黑树的性质

2.红黑树的实现

3.总结


1.概述

首先,我们来大致了解一下什么是红黑树

红黑树是一种自平衡的二叉查找树,是一种高效的查找树。红黑树具有良好的效率,它可在 O(logN) 时间内完成查找、增加、删除等操作。因此,红黑树在业界应用很广泛,比如 Java 中的 TreeMap,JDK 1.8 中的 HashMap。

1.1红黑树的性质

看过前面二叉查找树(即二叉搜索树)的同学都知道,普通的二叉查找树在极端情况下可退化成链表,此时的增删查效率都会比较低下。

为了避免这种情况,就出现了一些自平衡的查找树,比如 AVL,红黑树等。这些自平衡的查找树通过定义一些性质,将任意节点的左右子树高度差控制在规定范围内,以达到平衡状态。前面讲的AVL树实现自平衡的机制是设置一个平衡因子。

以红黑树为例,红黑树通过如下的性质定义实现自平衡:

  • 所有节点都有两种颜色:红色或黑色。
  • 根节点是黑色。
  • 所有null视为黑色。
  • 每个红色节点必须有两个黑色的子节点,即红色节点不能相邻(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
  • 从根节点到任意一个叶子节点,路径中的黑色节点数目一样(黑色完美平衡,简称黑高)。

有了上面的几个性质作为限制,即可避免二叉查找树退化成单链表的情况。

但是,仅仅避免这种情况还不够,这里还要考虑某个节点到其每个叶子节点路径长度的问题。

如果某些路径长度过长,那么,在对这些路径上的节点进行增删查操作时,效率也会大大降低。

这个时候性质4和性质5用途就凸显了,有了这两个性质作为约束,即可保证任意节点到其每个叶子节点路径最长不会超过最短路径的2倍。原因如下:

当某条路径最短时,这条路径必然都是由黑色节点构成。当某条路径长度最长时,这条路径必然是由红色和黑色节点相间构成(性质4限定了不能出现两个连续的红色节点)。而性质5又限定了从任一节点到其每个叶子节点的所有路径必须包含相同数量的黑色节点。此时,在路径最长的情况下,路径上红色节点数量 = 黑色节点数量。该路径长度为两倍黑色节点数量,也就是最短路径长度的2倍。

下面看几个红黑树:

2.红黑树的实现

下面来看一下红黑树的实现

package Tree;
/**红黑树的相关操作*/
public class L4_RBTree {enum Color{RED,BLACK}private Node root;private static class Node {int key;Object value;Node left;Node right;Node parent;//父节点Color color = Color.RED;//颜色public Node(int key, Object value) {this.key = key;this.value = value;}//是否是左孩子boolean isLeftChild(){return parent != null && parent.left == this;}//找叔叔结点Node isUncle(){if (parent == null || parent.parent == null){return null;}if (parent.isLeftChild()){return parent.parent.right;}else {return parent.parent.left;}}//找兄弟Node sibling(){if (parent == null){return null;}if (this.isLeftChild()){return parent.right;}else {return parent.left;}}}//判断是不是红色boolean isRed(Node node){return node != null && node.color == Color.RED;}//判断是不是黑色boolean isBlack(Node node){return  !isRed(node);}/*** 右旋* 要对parent进行处理* 选转后新根的父子关系* */private void rightRotate(Node node){Node nodeParent = node.parent;Node nodeLeft = node.left;Node nodeLeftRight = nodeLeft.right;if (nodeLeftRight != null){nodeLeftRight.parent = node;}nodeLeft.right = node;nodeLeft.parent = nodeParent;node.left = nodeLeftRight;node.parent = nodeLeft;if (nodeParent == null){root = nodeLeft;}else if (nodeParent.left == node){nodeParent.left = nodeLeft;}else {nodeParent.right = nodeLeft;}}//左旋private void leftRotate(Node node){Node nodeParent = node.parent;Node nodeRight = node.right;Node nodeRightLeft = nodeRight.left;if (nodeRightLeft != null){nodeRightLeft.parent = node;}nodeRight.left = node;nodeRight.parent = nodeParent;node.right = nodeRightLeft;node.parent = nodeRight;if (nodeParent == null){root = nodeRight;}else if (nodeParent.left == node){nodeParent.left = nodeRight;}else {nodeParent.right = nodeRight;}}/**新增或更新*/public void put(int key,Object value){Node p = root;Node parent = null;while (p != null){if (key < p.key){p = p.left;} else if(p.key < key){p = p.right;} else {p.value = value;return;}}Node inserted = new Node(key,value);if (parent == null){root = inserted;}else if (key < parent.key){parent.left = inserted;inserted.parent = parent;}else {parent.right = inserted;inserted.parent = parent;}fixRedRed(inserted);}/**节点调整*/private void fixRedRed(Node x){//插入节点是跟节点if (x == root){x.color = Color.BLACK;return;}//插入节点的父亲是黑色if (isBlack(x.parent)){return;}//红红相邻且叔叔为红Node parent = x.parent;Node uncle = x.isUncle();Node grandparent = parent.parent;if (isRed(uncle)){parent.color = Color.BLACK;uncle.color = Color.BLACK;grandparent.color = Color.RED;fixRedRed(grandparent);return;}//红红相邻且叔叔为黑if(parent.isLeftChild() && x.isLeftChild()){//LLparent.color = Color.BLACK;grandparent.color = Color.RED;rightRotate(grandparent);}else if (parent.isLeftChild() && !x.isLeftChild()){//LRleftRotate(parent);x.color = Color.BLACK;grandparent.color = Color.RED;rightRotate(grandparent);} else if (!parent.isLeftChild() && !x.isLeftChild()){//RRparent.color = Color.BLACK;grandparent.color = Color.RED;leftRotate(grandparent);}else {//RLrightRotate(parent);x.color = Color.BLACK;grandparent.color = Color.RED;leftRotate(grandparent);}}/**删除*/public void remove(int key){Node deleted = find(key);if (deleted == null){return;}doRemove(deleted);}private void doRemove(Node deleted){Node replaced = findReplaced(deleted);Node parent = deleted.parent;if (replaced == null){//没有孩子//删除的是根节点if (deleted == root){root = null;}else {if(isBlack(deleted)){//复杂调整fixDoubleBlack(deleted);}else {// 无需处理}if (deleted.isLeftChild()){parent.left = null;}else {parent.right = null;}deleted.parent = null;}return;}//有一个孩子if (deleted.left == null || deleted.right == null){//删除的是根节点if (deleted == null){root.key = replaced.key;root.value = replaced.value;root.left = root.right = null;}else {if (deleted.isLeftChild()){parent.left = replaced;}else {parent.right = replaced;}replaced.parent = parent;deleted.left = deleted.right = deleted.parent = null;//有助于垃圾回收if (isBlack(deleted) && isBlack(replaced)){//复杂处理fixDoubleBlack(replaced);}else {replaced.color = Color.BLACK;}}return;}//有两个孩子(是一种转换的操作)int t = deleted.key;deleted.key = replaced.key;replaced.key = t;Object v = deleted.value;deleted.value = replaced.value;replaced.value = v;doRemove(replaced);}/**遇到双黑的不平衡的复杂处理* x表示待调整的结点* */private void fixDoubleBlack(Node x){if (x == root){return;}Node parent = x.parent;Node sibling = x.sibling();//被调整节点的兄弟节点为红色if (isRed(sibling)){if (x.isLeftChild()){leftRotate(parent);}else {rightRotate(parent);}parent.color = Color.RED;sibling.color = Color.BLACK;fixDoubleBlack(x);return;}//兄弟是黑色且两个侄子都是黑色if (sibling != null){if (isBlack(sibling.left) && isBlack(sibling.right)){sibling.color = Color.RED;if (isRed(parent)){parent.color = Color.BLACK;}else {fixDoubleBlack(parent);}}//兄弟是黑色但是侄子有红色else {//LLif(sibling.isLeftChild() && isRed(sibling.left)){rightRotate(parent);sibling.left.color = Color.BLACK;sibling.color = parent.color;parent.color = Color.BLACK;}//LRelse if (sibling.isLeftChild() && isRed(sibling.right)){sibling.right.color = parent.color;leftRotate(sibling);rightRotate(parent);parent.color = Color.BLACK;}//RLelse if (!sibling.isLeftChild() && isRed(sibling.left)){sibling.left.color = parent.color;rightRotate(sibling);leftRotate(parent);parent.color = Color.BLACK;}//RRelse {leftRotate(parent);sibling.right.color = Color.BLACK;sibling.color = parent.color;parent.color = Color.BLACK;}}}else {fixDoubleBlack(parent);}}/**找到要删除的结点*/private Node find(int key){Node p = root;while (p != null){if (key < p.key){p = p.left;}else if (key > p.key){p = p.right;}else {return p;}}return null;}/**查找剩余结点*/private Node findReplaced(Node deleted){if (deleted.left == null && deleted.right == null){return null;}else if (deleted.left == null){return deleted.right;}else if(deleted.right == null){return deleted.left;}Node s = deleted.right;while (s.left != null){s = s.left;}return s;}
}

3.总结

有一说一,这红黑树确实比前面的几种树要难一点,主要是它的属性太多,限制太多。说实话,这篇文章中仅仅只是简单的介绍了一下红黑树,实现了一下红黑树的相关操作,但是红黑树的增加和删除中的一些操作没有细讲,这个有时间了我后面会单独再出一篇红黑树的补充文章然后细讲,这篇就这样了吧。

最后说一点,对于这种类似于链式的结构(实际是树形结构),我们要掌握它的定义,条件,然后画图,然后对照图来进行相关的操作,然后再用代码去实现,这样好一点,而不是凭空想象着去写,那样是写不出来的。

相关文章:

数据结构与算法——19.红黑树

这篇文章我们来讲一下红黑树。 目录 1.概述 1.1红黑树的性质 2.红黑树的实现 3.总结 1.概述 首先&#xff0c;我们来大致了解一下什么是红黑树 红黑树是一种自平衡的二叉查找树&#xff0c;是一种高效的查找树。红黑树具有良好的效率&#xff0c;它可在 O(logN) 时间内完…...

js题解(三)

文章目录 柯里化模块乘法改变上下文 柯里化 已知 fn 为一个预定义函数&#xff0c;实现函数 curryIt&#xff0c;调用之后满足如下条件&#xff1a; 1、返回一个函数 a&#xff0c;a 的 length 属性值为 1&#xff08;即显式声明 a 接收一个参数&#xff09; 2、调用 a 之后&a…...

CompletableFuture异步回调

CompletableFuture异步回调 CompletableFutureFuture模式CompletableFuture详解1.CompletableFuture的UML类关系2.CompletionStage接口3.使用runAsync和supplyAcync创建子任务4.设置子任务回调钩子5.调用handle()方法统一处理异常和结果6.线程池的使用 异步任务的串行执行thenA…...

Python中匹配模糊的字符串

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 如何使用thefuzz 库&#xff0c;它允许我们在python中进行模糊字符串匹配。 此外&#xff0c;我们将学习如何使用process 模块&#xff0c;该模块允许我们在模糊…...

PHP图片文件管理功能系统源码

文件图库管理单PHP源码直接解压就能用&#xff0c;单文件&#xff0c;indexm.php文件可以重新命名&#xff0c;上传到需要访问的目录中&#xff0c; 可以查看目录以及各个文件&#xff0c;图片等和下载及修改管理服务。 源码下载&#xff1a;https://download.csdn.net/downloa…...

(枚举 + 树上倍增)Codeforces Round 900 (Div. 3) G

Problem - G - Codeforces 题意&#xff1a; 思路&#xff1a; 首先&#xff0c;目标值和结点权值是直接联系的&#xff0c;最值不可能直接贪心&#xff0c;一定是考虑去枚举一些东西&#xff0c;依靠这种枚举可以遍历所有的有效情况&#xff0c;思考的方向一定是枚举 如果去…...

websocket逆向【python实现websocket拦截】

python实现websocket拦截 前言一、拦截的优缺点优点:缺点:二、实现方法1.环境配置2.代码三、总结前言 开发者工具F12,筛选ws后,websocket的消息是这样显示的,如何获取这里面的消息呢? 以下是本篇文章正文内容 一、拦截的优缺点 主要讲解一下websocket拦截的实现,现在…...

软件测试自动化的成本效益分析

随着软件测试技术的发展&#xff0c;人们已经从最初的手工测试转变为手工和自动化技术相结合的测试方法。目前&#xff0c;人们更多的是关心自动化测试框架、自动化测试工具以及脚本研究等技术方面&#xff0c;而在软件自动化测试方案的效益分析方面涉及较少。 软件测试的目的是…...

【Java】状态修饰符 final static

目录 final 修饰我们的成员方法、成员变量、类 示例代码&#xff1a; final 修饰的局部变量 示例代码&#xff1a; static 示例代码&#xff1a; static 访问特点&#xff1a; 示例代码&#xff1a; static关键字的用途 示例代码&#xff1a; static 修饰常量 示例…...

笔试编程ACM模式JS(V8)、JS(Node)框架、输入输出初始化处理、常用方法、技巧

目录 考试注意事项 先审完题意&#xff0c;再动手 在本地编辑器&#xff08;有提示&#xff09; 简单题515min 通过率0%&#xff0c;有额外log 常见输入处理 str-> num arr&#xff1a;line.split( ).map(val>Number(val)) 初始化数组 new Array(length).fill(v…...

learn掩码张量

目录 1、什么是掩码张量 2、掩码张量的作用 3、代码演示 &#xff08;1&#xff09;、定义一个上三角矩阵&#xff0c;k0或者 k默认为 0 &#xff08;2&#xff09;、k1 &#xff08;3&#xff09;、k-1 4、掩码张量代码实现 &#xff08;1&#xff09;、输出效果 &…...

激活函数介绍

介绍 神经网络当中的激活函数用来提升网络的非线性&#xff0c;以增强网络的表征能力。它有这样几个特点&#xff1a;有界&#xff0c;必须为非常数&#xff0c;单调递增且连续可求导。我们常用的有sigmoid或者tanh&#xff0c;但我们都知道这两个都存在一定的缺点&#xff0c…...

docker方式启动一个java项目-Nginx本地有代码,并配置反向代理

文章目录 案例导入说明1.安装MySQL1.1.准备目录1.2.运行命令1.3.修改配置1.4.重启 2.导入SQL3.导入Demo工程3.1.分页查询商品&#xff08;仔细看代码&#xff0c;很多新的MP编程技巧&#xff09;3.2.新增商品3.3.修改商品3.4.修改库存3.5.删除商品3.6.根据id查询商品3.7.根据id…...

前端和后端是Web开发选哪个好?

前端和后端是Web开发中的两个不同的领域&#xff0c;哪一种更适合学习&#xff1f;前景更广呢&#xff1f; 一、引言 Web前端开发就像装饰房间的小瓦匠&#xff0c;勤勤恳恳&#xff0c;仔仔细细&#xff0c;粉饰墙壁&#xff0c;妆点家具。会 HTML,CSS&#xff0c;懂点 JS。…...

HTTP协议,请求响应

、概述 二、HTTP请求协议 三、HTTP响应协议 四、请求数据 1.简单实体参数 RequestMapping("/simpleParam")public String simpleParam(RequestParam(name "name" ,required false ) String username, Integer age){System.out.println (username "…...

idea配置文件属性提示消息解决方案

在项目文件路径下找到你没有属性提示消息的文件 选中&#xff0c;ok即可 如果遇到ok无法确认的情况&#xff1a; 在下图所示位置填写配置文件名称即可...

EdgeView 4 for Mac:重新定义您的图像查看体验

您是否厌倦了那些功能繁杂、操作复杂的图像查看器&#xff1f;您是否渴望一款简单、快速且高效的工具&#xff0c;以便更轻松地浏览和管理您的图像库&#xff1f;如果答案是肯定的&#xff0c;那么EdgeView 4 for Mac将是您的理想之选&#xff01; EdgeView 4是一款专为Mac用户…...

流程自动化(RPA)的好处有哪些?

流程自动化&#xff08;RPA&#xff09;是一种通过软件机器人实现业务流程自动化的技术。它可以模拟人类在计算机上执行的操作&#xff0c;从而自动化重复性、繁琐的任务&#xff0c;提高工作效率和准确性。流程自动化&#xff08;RPA&#xff09;的好处很多&#xff0c;下面我…...

医学影像系统【简称PACS】源码

PACS(Picture Archiving and Comuniations Systems)即PACS&#xff0c;图像存储与传输系统&#xff0c;是应用于医院中管理医疗设备如CT&#xff0c;MR等产生的医学图像的信息系统。目标是支持在医院内部所有关于图像的活动&#xff0c;集成了医疗设备&#xff0c;图像存储和分…...

大家都在用哪些敏捷开发项目管理软件?

敏捷开发是一种以人为核心、迭代、循序渐进的开发方法。 敏捷开发的特点是高度灵活性和适应性、迭代式开发。 敏捷开发方法强调快速响应变化&#xff0c;因此它具有高度的灵活性和适应性。开发团队可以根据客户需求和市场变化快速调整开发计划和产品功能&#xff0c;以确保产品…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...