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

【C++】红黑树模拟实现插入功能(包含旋转和变色)

红黑树模拟实现并封装为map和set

  • 前言
  • 正式开始
    • 红黑树概念
    • 红黑树基本要求
    • 大致框架
      • 树节点
    • 调整红黑树使其平衡
      • 第一种:cur红,p红,g黑,u存在且为红
      • 第二种:cur红,p红,g黑,u不存在或为黑
        • 左左,右右:单旋 + 变色
        • 左右,右左:双旋 + 变色

在这里插入图片描述

前言

本篇主要讲解红黑树的模拟实现,实现之后再封装成map和set。

红黑树中所用到的旋转功能均源自我的上一篇博客,本篇中不会再详谈旋转,若对于旋转不了解的同学可以先看看上一篇博客:【C++】AVL树模拟实现插入功能

正式开始

前一篇的AVL树,是严格平衡的一棵二叉搜索树,也就是每个节点的做右子树高度差不超过1。

红黑树,在二叉搜索树的基础上,多了一个条件:最长路径不超过最短路径的2倍。没有AVL树那么严格,但是也是近似平衡的。

同AVL树相比,插入同样的数据,AVL树旋转更多,红黑树旋转更少,红黑树的优势就在于此,这一点比AVL树优很多,毕竟每次旋转的时候还是要动不少指针的。

但同时红黑树也有一丁点的劣势,查找效率比AVL树低一丢丢,因为极端情况下,也就是最长路径就是最短路径的2倍时,当我们正好要查找最长路径的叶子节点时,速度比AVL树慢了一半。但是虽说是一半,那也只是 l o g 2 N log_2N log2N的一半,如果存储10亿个数,查找最边上的节点,也就查找30次就能找到,因为对于AVL树来说几乎是完全二分的,那么红黑树的最坏情况就是60次喽,但是这样二者相差可以说是忽略不计的。

所以考虑整体情况的话,还是红黑树更胜一筹。不然STL中的map和set底层用的就不是红黑树了。

红黑树概念

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

先来一棵树看看:
在这里插入图片描述

看起来花里胡哨的,不要怕,纸老虎。

红黑树基本要求

红黑树实现的时候不是按照AVL树那样依靠平衡因子来实现的。而是通过控制节点是红色还是黑色来实现的,不然也就不叫红黑树了。

但是对于节点是红色还是黑色是有要求的:

  1. 每个节点非黑即红
  2. 根节点是黑色的
  3. 红色节点的两个子节点为黑色的,即树中没有连续的红色结点
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。即每条路径上黑色结点数相等。
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)。

第五点其实没什么用,是指空节点是黑色节点,当树是空树的时候正好可以和第二条对应。树是空树,根节点就是空的,空节点是黑色节点,那么根节点也就是黑色结点了。

我再把上面那张图拿出来,各位对着前四点看看:
在这里插入图片描述

每个结点满足上述条件后就能够保证最长路径不超过最短路径的二倍了。各位想想为什么。

上述条件中可没有说不能有连续的黑色节点,所以最短路径就可以是连续黑色结点所组成的路径,最长路径就是一黑一红一黑一红…组成的路径。那么当到达极限时,最长路径就正好是最短路径的二倍。

再问个问题:
上图中,一共有几条路径?

答案是:11条。
有的同学可能懵了,不要懵。
上面数路径是要数到空节点的,上面一共有11个空节点,所以也就是11条路径。
这里也是第五点的一个好处,方便我们数路径,可以看到图中空节点写的是NIL,红黑树这里把空节点叫做NIL节点。

废话不多说了,我们先把树大致框架搞出来。

大致框架

树节点

红黑树树节点中包含的东西如下:
在这里插入图片描述

构造函数为:
在这里插入图片描述
颜色暂时不给,等会插入的时候会再说。

再来写树。

初始就一根:
在这里插入图片描述

然后把二叉搜索树的基本的插入功能写出来:
在这里插入图片描述

上面我把调整树结构的留下,这里细讲。

调整红黑树使其平衡

若插入新节点后的树结构不符合了前面说的那四条规则,此时就要调整。

但是插入节点要确定一下其颜色,如何确定呢?
看第三条和第四条规定。不能有连续的红色节点,所有路径黑色节点数量相同。

所以如果我们插入红色节点,影响的就只是当前插入节点所处的路径,因为红色节点不相连即可。如果我们插入黑色结点,影响的就是整棵树了,因为当前路径加一个黑的,其他路径就都要加一个黑的。显然,我们要选就选红色的插入,千万不敢选黑色的插,一选黑色就要调整棵树,选了红色只调当前路径就够了。

那么调整之前,我得先声明几个节点的名字

  1. cur节点,就是新插入的节点,等会在调整的过程中我以cur表示。
  2. . 父(father)节点 / 双亲(parent)节点, 等会在调整的过程中我以字母p表示。
  3. 叔叔(uncle)节点,就是父节点的兄弟,等会在调整的过程中我以字母u表示。
  4. 爷爷(grandfather)节点,就是父节点的父节点,等会在调整的过程中我以字母g表示。

先来个图看看:
在这里插入图片描述

那么调整可分为两大种情况。

第一种:cur红,p红,g黑,u存在且为红

第二种:cur红,p红,g黑,u不存在或为黑

说一下为啥是上面两种。
首先我们插入的一定是红色节点。

  1. 树为空的时候,不需要调整,直接让根搞成黑色节点。
  2. 插入节点为红色,p为黑色,此时不需要调整,直接插入即可。因为前面也说了,极限长度是黑红黑红这样规律走下去的,当插入的是红色的时候,不会出现到达最长路径的情况。所以不需要调整。
  3. 插入节点为红色,p为红色,违反第三条规定,此时就需要调整。而且当p为红色的时候,g一定为黑色,因为原树要保证是红黑树,所以当p是红色的时候,不能有连续的红色节点,所以g就一定是黑色的。

然后将第三点的调整分为两大种,至于为什么,等会将调整的时候就知道了,这是前人总结出来的规律。

那么就开始将调整。
如果你对于AVL树插入时的旋转非常熟悉了,那么这里红黑树插入的调整自然也不在话下。

正式开始调整:

第一种:cur红,p红,g黑,u存在且为红

调整方式为:pu变黑g变红,cur指向g继续向上调整,遇到树根为止,将树根改为黑。
即只有变色。

先来两个例子:

调整一次:
在这里插入图片描述

调整两次:
在这里插入图片描述

不用往下画了,再往下就是调整三、四、五……等等次数了。是有规律的,上面两个都是具象图,下来我把抽象图给大家,各位参考参考:
在这里插入图片描述

附上我手画的图:
在这里插入图片描述
比较潦草,主要是方便我看一下。

这里不管cur在p左边还是右边,调整方式都是一样的,记住最开始写的调整方式就行:

调整方式为:pu变黑g变红,cur指向g继续向上调整,遇到树根为止,将树根改为黑。
即只有变色。

第二种:cur红,p红,g黑,u不存在或为黑

第二大种就要用到旋转了,但还要再分为两小种。

第一小种:p在g左且cur在p左,或者p在g右且cur在p右。此种情况下为单旋。
对应的图为:
在这里插入图片描述
在这里插入图片描述

第二小种:p在g左且cur在p右,或者p在g右且cur在p左。此种情况下为双旋。
对应图为:
在这里插入图片描述
在这里插入图片描述

然后再细谈:

左左,右右:单旋 + 变色

单旋 + 变色。
和AVL树一样,左左就右单旋,右右就左单旋。

我就只画左左的,还是先给两个实例的图,再给抽象图。

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

抽象图:
在这里插入图片描述

左右,右左:双旋 + 变色

单旋 + 变色。
和AVL树一样,左右就左右双旋,右左就右左双旋。

实例:
在这里插入图片描述
在这里插入图片描述

抽象图:
在这里插入图片描述

手画图:
在这里插入图片描述

总结一下:
红黑树调节平衡关键是看叔叔。
u存在且为红,变色继续往上处理。
u不存在 或者 存在且为黑,旋转+变色。其中旋转又分单旋和双旋,若p、cur在单侧,就单旋,若p、cur一左一右或一右一左就双旋。

上面的三种情况搞完就可以写代码了。

调节平衡的代码如下:
在这里插入图片描述

其中我光用了单旋,没有用双旋,因为双旋就是两个单旋。
单旋代码:
在这里插入图片描述

可以写一个中序遍历来简单打印一下结果:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

但是简单的中序遍历可不够,来写一个专门用来检查是否平衡的函数。

在这里插入图片描述

在这里插入图片描述

到此结束。。。

相关文章:

【C++】红黑树模拟实现插入功能(包含旋转和变色)

红黑树模拟实现并封装为map和set 前言正式开始红黑树概念红黑树基本要求大致框架树节点树 调整红黑树使其平衡第一种:cur红,p红,g黑,u存在且为红第二种:cur红,p红,g黑,u不存在或为黑…...

Pads输出器件坐标文件时,如何更改器件坐标精度

相信对于用pads软件的工程师么,在完成PCB设计的时候都需要输出生产文件给板厂和贴片厂,今天我们需要给大家介绍的是如何在在pads软件上面输出器件坐标文件以及如何更改器件坐标文件的精度。 首先我们需要点击工具-基本脚本-基本脚本接下来会跳到下面这个…...

Vuejs3父组传值给子组件

父组件代码 <script setup> import TextProps from ./components/TextProps.vue; import { reactive } from vue;const queryobj reactive({"a":1, "b":1}); const aryobj reactive([1,2,3]);</script><template><div class"…...

竞赛项目 深度学习的智能中文对话问答机器人

文章目录 0 简介1 项目架构2 项目的主要过程2.1 数据清洗、预处理2.2 分桶2.3 训练 3 项目的整体结构4 重要的API4.1 LSTM cells部分&#xff1a;4.2 损失函数&#xff1a;4.3 搭建seq2seq框架&#xff1a;4.4 测试部分&#xff1a;4.5 评价NLP测试效果&#xff1a;4.6 梯度截断…...

【剑指 の 精选】热门状态机 DP 运用题

题目描述 这是 LeetCode 上的 「剑指 Offer II 091. 粉刷房子」 &#xff0c;难度为 「中等」。 Tag : 「状态机 DP」、「动态规划」 假如有一排房子&#xff0c;共 n 个&#xff0c;每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种&#xff0c;你需要粉刷所有的房子…...

自动化实践-全量Json对比在技改需求提效实践

1 背景 随着自动化测试左移实践深入&#xff0c;越来越多不同类型的需求开始用自动化测试左移来实践&#xff0c;在实践的过程中也有了新的提效诉求&#xff0c;比如技改类的服务拆分项目或者BC流量拆分的项目&#xff0c;在实践过程中&#xff0c;这类需求会期望不同染色环境…...

【Matlab】PSO优化(单隐层)BP神经网络

上一篇博客介绍了BP-GA&#xff1a;BP神经网络遗传算法(BP-GA)函数极值寻优——非线性函数求极值&#xff0c;本篇博客将介绍用PSO&#xff08;粒子群优化算法&#xff09;优化BP神经网络。 1.优化思路 BP神经网络的隐藏节点通常由重复的前向传递和反向传播的方式来决定&#…...

创建型模式-原型模式

文章目录 一、原型模式1. 概述2. 结构3. 实现4. 案例1.5 使用场景1.6 扩展&#xff08;深克隆&#xff09; 一、原型模式 1. 概述 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 2. 结构 原型模式包含如下角色&#xff1a; …...

JS逆向系列之猿人学爬虫第11题 - app抓取 - so文件协议破解

题目地址 http://match.yuanrenxue.com/match/11这是个app题目,先下载下来安装到测试手机上 安装完成后的app界面长这样 打开之后是这样的: 要求已经简单明了了。 二话不说先反编译app 不出意外的是没出意外,源代码里面没啥混淆,所有东西都展示的明明白白的。 "…...

c基础扫雷

和三子棋一样&#xff0c;主函数先设计游戏菜单界面&#xff0c;这里就不做展示了。 初始化棋盘 初级扫雷大小为9*9的棋盘&#xff0c;但排雷是周围一圈进行排雷(8格)&#xff0c;而边界可能会越界。数组扩大了一圈,行和列都加了2&#xff0c;所以我们用一个11*11的数组来初始化…...

端点中心(Endpoint Central)的软件许可证管理

软件许可证管理 &#xff08;SLM&#xff09; 是从单个控制台管理整个组织中使用的软件许可证的过程。软件许可证是由软件发行商或分销商制作的法律文件&#xff0c;提供有关软件使用和分发的规则和指南&#xff0c;本文档通常包含条款和条件、限制和免责声明。 软件许可证管理…...

SpringCloud源码探析(九)- Sentinel概念及使用

1.概述 在微服务的依赖调用中&#xff0c;若被调用方出现故障&#xff0c;出于自我保护的目的&#xff0c;调用方会主动停止调用&#xff0c;并根据业务需要进行对应处理&#xff0c;这种方式叫做熔断&#xff0c;是微服务的一种保护方式。为了保证服务的高可用性&#xff0c;…...

nodejs+vue+elementui美食网站的设计与实现演示录像2023_0fh04

本次的毕业设计主要就是设计并开发一个美食网站软件。运用当前Google提供的nodejs 框架来实现对美食信息查询功能。当然使用的数据库是mysql。系统主要包括个人信息修改&#xff0c;对餐厅管理、用户管理、餐厅信息管理、菜系分类管理、美食信息管理、美食文化管理、系统管理、…...

Mysql 数据库增删改查

MySQL是目前最流行的关系型数据库。以下是MySQL数据库的增删改查操作。 1.数据库连接 在进行增删改查操作之前&#xff0c;需要先连接MySQL数据库。使用以下命令进行连接&#xff1a; import mysql.connectormydb mysql.connector.connect(host"localhost",user&…...

【深度学习注意力机制系列】—— ECANet注意力机制(附pytorch实现)

ECANet&#xff08;Efficient Channel Attention Network&#xff09;是一种用于图像处理任务的神经网络架构&#xff0c;它在保持高效性的同时&#xff0c;有效地捕捉图像中的通道间关系&#xff0c;从而提升了特征表示的能力。ECANet通过引入通道注意力机制&#xff0c;以及在…...

python爬虫的简单实现

当涉及网络爬虫时&#xff0c;Python中最常用的库之一是requests。它能够发送HTTP请求并获取网页内容。下面是一个简单的示例&#xff0c;展示如何使用requests库来获取一个网页的内容&#xff1a; import requests 指定要爬取的网页的URL url ‘https://example.com’ 发…...

如何正确的向chatgpt提问?

有没有发现&#xff0c;在使用ChatGPT的时候&#xff0c;他回答的一些问题并不是我们想要的甚至有的时候出现牛头不对马嘴的情况。 这时候就会感慨一句&#xff0c;人工智能也不怎么样嘛! 但是&#xff0c;有没有想过&#xff0c;是自己问的问题太宽泛&#xff0c;没有问到点上…...

一键部署 Umami 统计个人网站访问数据

谈到网站统计&#xff0c;大家第一时间想到的肯定是 Google Analytics。然而&#xff0c;我们都知道 Google Analytics 会收集所有用户的信息&#xff0c;对数据没有任何控制和隐私保护。 Google Analytics 收集的指标实在是太多了&#xff0c;有很多都是不必要的&#xff0c;…...

java种的hutool库接口说明和整理

1. Hutool库基本介绍 1.1. 地址 官网地址&#xff1a;https://www.hutool.cn/ 1.2. 基本介绍 Hutool是一个小而全的Java工具类库&#xff0c;通过静态方法封装&#xff0c;降低相关API的学习成本&#xff0c;提高工作效率&#xff0c;使Java拥有函数式语言般的优雅&#xf…...

控制国外各类电液伺服阀放大器

控制通用型不带反馈信号输入的伺服阀放大器&#xff0c;对射流管式电液伺服阀、喷嘴挡板式电液伺服阀及国外各类电液伺服阀进行控制。 通过系统参数有10V和4~20mA输入指令信号选择&#xff1b; 供电电源: 24VDC&#xff08;标准&#xff09; 输出电流&#xff1a;最大可达10…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...