《红黑树实现》

引言:

上次我们学习了比二叉搜索树更高效的平衡二叉搜索树(AVL树),这次我们要学习的是另外一种对二叉搜索树的优化后的红黑树

一:红黑树概念:

红黑树是一棵二叉搜索树,他的每个结点增加一个存储位来表示结点的颜色,可以是红色或者黑色。通过对任何一条从根到叶子的路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2倍,因而是接近平衡的。

(1)红黑树规则:

  1. 每个结点不是红色就是黑色。
  2. 根结点是黑色的。
  3. 如果一个结点是红色的,则它的两个孩子结点必须是黑色的,也就是说任意一条路径不会有连续的红色结点。
  4. 对于任意一个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的黑色结点。

(2)思考:红黑树是如何确保最长路径不超过最短路径的2倍的?

  1. 由规则4可知,从根到NULL结点的每条路径都有相同数量的黑色结点,所以极端场景下,最短路径就就是全是黑色结点的路径,假设最短路径长度为bh(black height)
  2. 由规则2和规则3可知,任意一条路径不会有连续的红色结点,所以极端场景下,最长的路径就是一黑一红间隔组成,那么最长路径的长度为2*bh
  3. 综合红黑树的4点规则而言,理论上的全黑最短路径和一黑一红的最长路径并不是在每棵红黑树都存在的。假设任意⼀条从根到NULL结点路径的长度为x,那么bh <= h <= 2*bh

(3)红黑树效率:

假设N是红黑树中节点的数量,h为最短路径的长度,那么2^h - 1 <= N < 2 ^ (2 * h) - 1 ,由此推出h ≈ logN ,也就是意味着红黑树的增删查改最坏情况下也就是走最长路径时的效率也是logN
但红黑树的表达相对于AVL树要抽象一些,AVL树是通过高度差来直观地控制,红黑树通过4条规则的颜色约束,间接实现了近似平衡,他们效率都是同一档次,但是相对而言,插入相同数量的结点,红黑树的旋转次数是更少的,因为他对平衡的控制没那么严格。

在这里插入图片描述

二:红黑树的实现

1. 红黑树的结构:

跟前面实现的AVL树相比,红黑树的结构只是将平衡因子变为了颜色记录
在这里插入图片描述
在这里插入图片描述

2. 红黑树的插入:

(1)约定:

下面我们分析时的各节点命名:
下图中假设我们把新增结点标识为c(cur),c的父亲标识为p(parent),p的父亲标识为g(grandfather),p的兄弟标识为u(uncle)

(2)红黑树插入的大致流程:
  1. 插入一个值按二叉搜索树规则进行插入,插入后我们只需要观察是否符合红黑树的4条规则。
  2. 如果是空树插入,新增结点是黑色结点。如果是非空树插入,新增结点必须为红色结点,因为非空树插入,新增黑色结点就破坏了规则4,规则4是很难维护的。
  3. 非空树插入后,新增结点必须红色结点,如果父亲结点是黑色的,则没有违反任何规则,插入结束。
  4. 非空树插入后,新增结点必须红色结点,如果父亲结点是红色的,则违反规则3。进一步分析,c是红色,p为红,g必为黑,这三个颜色都固定了,关键的变化看u的情况,需要根据u分为以下几种情况分别处理。
(3)情况1:u存在且为红(只变色)

c为红,p为红,g为黑,u存在且为红,则将p和u变黑,g变红。在把g当做新的c,继续往上更新。
分析:因为pu都是红色,g是黑色,把pu变黑,左边子树路径各增加一个黑色结点,g再变红,相当于保持g所在子树的黑色结点的数量不变,同时解决了cp连续红色结点的问题,需要继续往上更新是因为,g是红色,如果g的父亲还是红色,那么就还需要继续处理;如果g的父亲是黑色,则处理结束了;如果g就是整棵树的根,再把g变回黑色。(因为根节点应该为黑色)
情况1只变色,不旋转。所以无论cp的左还是右,pg的左还是右,都是上面的变色处理方式。

a. 具体示例:

在这里插入图片描述

b. 抽象示例:

在这里插入图片描述

情况1代码实现:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(4)情况2:u 不存在或者存在且为黑(单旋+变色)
  1. c为红,p为红,g为黑,u不存在或者u存在且为黑
  2. u不存在,则c⼀定是新增结点,
  3. u存在且为黑,则c一定不是新增,c之前是黑色的,是在c的子树中插入,符合情况1,变色将c从黑色变成红色,更新上来的。
    分析:p必须变黑,才能解决连续红黑结点的问题,u不存在或者是黑色的,这里单纯的变色无法解决问题,需要旋转+变色。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

单旋+变色代码实现:

在这里插入图片描述

情况2:u 不存在或者 u 存在且为黑(双旋+变色)
  1. c为红,p为红,g为黑,u不存在或者u存在且为黑。
  2. u不存在,则c一定是新增结点,
  3. u存在且为黑,则c一定不是新增,c之前是黑色的,是在c的子树中插入,符合情况1,变色将c从黑色变成红色,更新上来的。
    分析:p必须变黑,才能解决,连续红色结点的问题,u不存在或者是黑色的,这里单纯的变色无法解决问题,需要旋转+变色。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

双旋+变色 代码实现:

在这里插入图片描述

当 p u 位置反过来时的代码:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. 红黑树的查找:

按二叉搜索树的逻辑实现即可,搜索效率为O(logN)
在这里插入图片描述
在这里插入图片描述

4. 红黑树的验证

这里获取最长路径和最短路径,检查最长路径不超过最短路径的2倍是不可行的,因为就算满足这个条件,红黑树也可能颜色不满足规则,当前暂时没出问题,后续继续插入还是会出问题的。所以我们还是去检查4点规则,满足这4点规则,一定能保证最长路径不超过最短路径的2倍。

  1. 规则1枚举颜色类型,天然实现保证了颜色不是黑色就是红色。
  2. 规则2直接检查根即可。
  3. 规则3前序遍历检查,遇到红色结点查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲的颜色就方便多了。
  4. 规则4前序遍历,遍历过程中用形参记录跟到当前结点的blackNum(黑色结点数量),前序遍历遇到黑色结点就++blackNum,走到空就计算出了一条路径的黑色结点数量。再任意一条路径黑色结点数量作为参考值,依次比较即可。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

5. 测试:

(1)插入及其合法性验证:

在这里插入图片描述

(2)性能测试:

在这里插入图片描述
在这里插入图片描述

完结!!!

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

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

相关文章

领域驱动设计(DDD)【23】之泛化:从概念到实践

文章目录 一 泛化基础&#xff1a;理解DDD中的核心抽象机制1.1 什么是泛化&#xff1f;1.2 为什么泛化在DDD中重要&#xff1f;1.3 泛化与特化的双向关系 二 DDD中泛化的实现形式2.0 实现形式概览2.1 类继承&#xff1a;最直接的泛化实现2.2 接口实现&#xff1a;更灵活的泛化方…

机箱流动空气热学仿真方案

机箱流动空气热学仿真方案(二维平面与三维) 一、物理模型与数学模型 1. 控制方程 流动与传热基本方程: 连续性方程:∇(ρu) = 0动量方程(Navier-Stokes):ρ(u∇)u = -∇p + μ∇u + F能量方程:ρcₚ(u∇)T = k∇T + Φ边界条件: 入口:速度入口(u=u₀, T=T₀)出口:压…

electron 如何配置 打开控制台

在 Electron 应用中&#xff0c;打开开发者工具&#xff08;即控制台&#xff09;通常有两种方式&#xff1a; 程序运行时手动打开 在 Electron 应用中&#xff0c;你可以通过编程方式打开开发者工具。这通常在你需要调试时非常有用。你可以在你的主进程&#xff08;通常是 ma…

MR7350用TTL刷机救砖过程

很久之前就买了一台Linksys的MR7350路由器&#xff0c;准备有OpenWRT的官方固件之后再拿它当轻NAS用&#xff0c;最近看到出了Snapshot版&#xff0c;于是就拿来刷机试试。经过我坚持不懈的折腾&#xff0c;终于把我的MR7350路由器刷成了砖&#xff0c;即便是通过开机过程中断电…

在NPU单算子(torch_npu )执行时如何进行性能优化?以MinerU为例

1 MinerU介绍 在AI技术快速发展的今天&#xff0c;大量非结构化数据的处理成为亟待解决的问题。尤其是PDF文档&#xff0c;作为最常见的文件格式之一&#xff0c;如何高效准确地提取其中的信息&#xff0c;成为了许多企业和研究机构的痛点。上海人工智能实验室&#xff08;上海…

鸿蒙OS开发IoT控制应用:从入门到实践

引言&#xff1a;万物互联时代的应用开发新范式 在物联网(IoT)技术迅猛发展的今天&#xff0c;智能设备数量呈指数级增长。据IDC预测&#xff0c;到2025年全球IoT连接设备数将达到416亿台。面对碎片化的IoT设备和多样化的控制需求&#xff0c;华为鸿蒙OS(HarmonyOS)应运而生&a…

五层网络模型:网络通信的核心框架

在网络通信的世界里&#xff0c;五层网络模型是一个基础而关键的概念。它帮助我们理解数据是如何在网络上从一个设备传输到另一个设备的。本文将详细介绍五层网络模型的每一层&#xff0c;以及它们在数据传输过程中的作用。 一、五层网络模型概述 五层网络模型是一种分层的网…

常见的强化学习算法分类及其特点

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是一种机器学习方法&#xff0c;通过智能体&#xff08;Agent&#xff09;与环境&#xff08;Environment&#xff09;的交互来学习如何采取行动以最大化累积奖励。以下是一些常见的强化学习算法分类及其特点&#…

【LeetCode 热题 100】438. 找到字符串中所有字母异位词——(解法三)不定长滑动窗口+数组

Problem: 438. 找到字符串中所有字母异位词 题目&#xff1a;给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 【LeetCode 热题 100】438. 找到字符串中所有字母异位词——&#xff08;解法一&…

求区间最大值

题目描述 给定一个长度为 N 的数列&#xff0c;和 M 次询问&#xff0c;求出每一次询问的区间内数字的最大值。 输入描述 第一行包含两个整数 N,M&#xff0c;分别表示数列的长度和询问的个数。 第二行包含 N 个整数&#xff08;记为&#x1d44e;&#x1d456;&#xff09;&am…

调试HDMI音频能8通道播放声音

一、使用场景 我们是通过rk主控的hdmi接口播放音视频给到ite68051芯片解析出8声道数据,分别通过4路i2s的数据脚给给到fpga去解析 调试步骤: 1.根据相关手册配置hdmi输出,hdmi声卡注册,如下: hdmi0_sound: hdmi0-sound {status = "disabled";compatible = &qu…

PowerBI 柱状图显示MoM销量环比示例,以及解决相同列值时设置柱子颜色的问题

先看效果: 假设有Sales表: 1. 我们先给它新增一个计算列&#xff0c;显示销售日期的年月 销售日期YYYYMM YEAR(Sales[销售日期])*100 MONTH(Sales[销售日期]) 2. 然后新增一个计算表&#xff0c;用于保存当前最大的销售日期&#xff0c;和上一个月的日期 DateComparisonT…

【docker】构建时使用宿主机的代理

docker构建过程中报错: pip 下载失败 解决办法:传递宿主机的代理 把宿主机的 HTTP_PROXY/HTTPS_PROXY 传进去,导致容器内的 pip 依然连不上代理,下载 build-dependencies(比如 setuptools)就会失败。 下面两步即可解决: Docker 构建阶段,127.0.0.1:7890 指向的是 容…

[Java 基础]算法

什么是算法 程序 数据结构 算法 算法&#xff08;Algorithm&#xff09;就是解决问题的步骤&#xff0c;就像做菜的食谱一样&#xff0c;告诉计算机一步一步如何完成任务。 例如&#xff1a; 排序算法&#xff1a;把一堆数字从小到大排列搜索算法&#xff1a;在一堆数据里…

C++理解for循环 计算题三

计算a的值 #include <iostream> using namespace std; int main() { int a0;for(int i0;i<3;i){for(int j0;j<3;j){aij;}}cout<<"a的值是 "<<a<<endl; return 0; } 计算a的值 #include <iostream> using namespace std; int …

梳理React中的fiber架构

文章目录 产生背景核心概念工作原理工作流程优势特点 产生背景 在React16之前使用的虚拟DOM是数组的形式&#xff0c;又因为React本身是应用级框架&#xff0c;状态改变后并不能准确知道是哪个组件发生了改变&#xff0c;只能对整个应用进行diff协调&#xff0c;受限于虚拟DOM…

Modbus 数据模型:线圈、寄存器与功能码详解(二)

三、Modbus 功能码详解 3.1 功能码分类与作用 Modbus 功能码是 Modbus 通信协议中的关键组成部分&#xff0c;它如同一个 “指令指挥官”&#xff0c;在通信事务处理中扮演着核心角色。功能码占用 1 个字节的空间&#xff0c;取值范围为 1 到 255 &#xff08;0x01 - 0xFF&am…

多表连接查询:语法、注意事项与最佳实践

&#x1f517; 多表连接查询&#xff1a;语法、注意事项与最佳实践 多表连接是 SQL 的核心能力&#xff0c;用于关联多个表的数据。以下是深度解析&#xff0c;涵盖语法规范、性能陷阱及实战技巧&#xff1a; &#x1f4dc; 一、多表连接语法大全 1. 显式连接&#xff08;推荐…

使用Calibre对GDS进行数据遍历

在芯片的GDS数据里&#xff0c;使用Calibre对数据进行处理是非常常见的操作&#xff0c;但是GDS是一种和常规设计结构不太一样的一种数据&#xff0c;这里&#xff0c;通过这个小小的科普文章&#xff0c;一起看看怎么样在GDS里边做数据漫游吧&#xff01;闲言少叙&#xff0c;…

PyQtNode Editor 第二篇自定义可视化视图

在第一篇博客中,我们已经完成了 PyQtNode Editor 的基础环境搭建,并深入解析了自定义图形场景QDMGraphicsScene的实现原理。那个带有网格背景的场景就像一张空白的图纸,现在我们要在这张图纸上开始绘制真正的节点系统。 今天我们将聚焦于节点编辑器的核心数据结构设计,实现…