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

news2025/2/11 15:46:30

红黑树模拟实现并封装为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一左一右或一右一左就双旋。

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

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

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

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

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

到此结束。。。

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

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

相关文章

selenium.webdriver Python爬虫教程

文章目录 selenium安装和使用 selenium安装和使用 pip install selenium 下载对应的浏览器驱动 实例化浏览器 from selenium import webdriverbrowser webdriver.Chrome()元素定位 控制浏览器

echart图案例

效果 代码&#xff1a; index.vue <template><div class"pageBox"><div class"oneLineBox"><div class"fourColorImgBox"><div class"titleBox">企业风险四色图</div><div class"conte…

蝉妈妈:2023年抖音电商半年报(附下载)

关于报告的所有内容&#xff0c;公众【营销人星球】获取下载查看 核心观点 平台流量竞争从愈发激烈变为趋于愈加缓和商家直攝总时长与观众君播总时长的总体趋势并没有愈加激烈&#xff0c;从23年上半年各自流量的同比增速来看&#xff0c;观众看摄总时长增速高于商家直攝总时…

AD域机器KMS自动激活

1、打开AD域控&#xff0c;点击DNS管理 2、创建其它记录 3、选择服务位置 SRV 4、输入相关信息 服务&#xff1a;_VLMCS协议&#xff1a;_TCP权重&#xff1a;100端口号&#xff1a;1688KMS服务器地址&#xff1a;10.3.0.211 5、成功&#xff0c;这时域内主机重启后&#xff0…

全自动猫砂盆市场调查:2022年全球全自动猫砂盆市场销售额达到了2.31亿美元

全自动猫砂盆是一种使用先进技术来自动清洁的猫砂盆。这些猫砂盆通常有传感器或计时器&#xff0c;可以检测到猫何时使用了猫砂盆并启动清洁程序。一些型号的猫砂盆还加入了自清洁机制&#xff0c;通过耙子或筛子来清除猫砂的结块和碎片。 全自动猫砂盆的设计旨在减少维护猫砂…

vue3+antv x6自定义节点样式

先大致定下节点样式&#xff0c;需要展示标题&#xff0c;输入/输出连接桩&#xff0c; 参考样子大概是 https://x6.antv.antgroup.com/examples/showcase/practices#class 这是根据antv x6配置 非自定义节点 图表案例 结果 数据格式大概是 nodes:[{title:鸟,id:node1,ports…

逐步分解,一文教会你如何用 jenkins+docker 实现主从模式

♥ 前 言 jenkins 主从模式想必大家都不陌生&#xff0c;大家在学习过程中为了学习方便都在自己本地搭建了 jenkins 环境&#xff0c;然后通过 javaweb 方式实现&#xff0c;对于 docker 下实现主从模式大家好像兴趣挺大。今天就通过这篇文章给大家讲讲怎么玩&#xff0c;希…

【JavaEE进阶】Bean 作用域和生命周期

文章目录 一. 关于Bean作用域的实例1. lombok2. 实例代码 二. 作用域定义1. Bean的六种作用域2. 设置作用域 三. Spring 执行流程和 Bean 的生命周期1. Spring 执行流程2. Bean生命周期 一. 关于Bean作用域的实例 注意在此例子中需要用到lombok 1. lombok lombok是什么? Lo…

android ndk clang交叉编译ffmpeg动态库踩坑

1.ffmpeg默认使用gcc编译&#xff0c;在android上无法使用&#xff0c;否则各种报错&#xff0c;所以要用ndk的clang编译 2.下载ffmpeg源码 修改configure文件&#xff0c;增加命令 cross_prefix_clang 修改以下命令 cc_default"${cross_prefix}${cc_default}" cxx…

Jmeter请求接口返回值乱码解决

乱码示例 解决步骤&#xff1a; 1.打开Jmeter安装目录下的bin目录&#xff0c;找到jmeter.properties 2.使用记事本或其他编译工具打开jmeter.properties文件&#xff0c;然后全局搜索sampleresult.default.encoding 3.在文件中添加sampleresult.default.encodingutf-8,保存…

C++入门篇7---string类

所谓的string类&#xff0c;其实就是我们所说的字符串&#xff0c;本质和c语言中的字符串数组一样&#xff0c;但是为了更符合C面向对象的特性&#xff0c;特地将它写成了一个单独的类&#xff0c;方便我们的使用 对其定义有兴趣的可以去看string类的文档介绍&#xff0c;这里…

【在树莓派上安装cpolar内网穿透实战】

文章目录 前言1.在树莓派上安装cpolar2.查询cpolar版本号3.激活本地cpolar客户端4.cpolar记入配置文件 前言 树莓派作为一个超小型的电脑系统&#xff0c;虽然因其自身性能所限&#xff0c;无法如台式机或笔记本等准系统一样&#xff0c;运行大型软件或程序&#xff08;指望用…

ecology-自定义浏览按钮实现多处引用可定制不同查询条件。

1.新建ecode代码&#xff0c;前置加载&#xff0c;代码内容&#xff1a; ecodeSDK.overwritePropsFnQueueMapSet(WeaBrowser,{ //组件名fn:(newProps)>{ //newProps代表组件参数 if(newProps.type162 || newProps.type161){//console.log("自定义浏览框");if(!ne…

日常BUG——使用Long类型作id,后端返回给前段后精度丢失问题

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 数据库long类型Id: 前端返回的Id实体类: Data ApiModel("xxx") public class …

数据结构顺序表

今天主要讲解顺序表&#xff0c;实现顺序表的尾插&#xff0c;头插&#xff0c;头删&#xff0c;还有尾删等操作&#xff0c;和我们之前写的通讯录的增删查改有类似的功能。接下来让我们开始我们的学习吧。 1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特…

保证接口数据安全的10种方式

我们日常开发中&#xff0c;如何保证接口数据的安全性呢&#xff1f;个人觉得&#xff0c;接口数据安全的保证过程&#xff0c;主要体现在这几个方面&#xff1a;一个就是数据传输过程中的安全&#xff0c;还有就是数据到达服务端&#xff0c;如何识别数据&#xff0c;最后一点…

安达发|企业如何提高生产实现精细化管理

随着市场竞争的加剧&#xff0c;企业如何提高生产效率和降低成本成为了关键。本文将探讨生产计划排程表的制定方法&#xff0c;帮助企业实现精细化管理&#xff0c;提升竞争力。 在传统的生产管理中&#xff0c;企业往往依赖于人工经验和直觉来制定生产计划&#xff0c;导致生产…

docker复现Nginx配置漏洞

目录 1.docker环境搭建 2.复现过程 2.1CRLF(carriage return/line feed)注入漏洞 2.2.目录穿越 2.3.add_header覆盖 1.docker环境搭建 1.安装docker apt-get update apt-get install docker.ioyum install docker.io 2.下载并解压docker环境Nginx配置漏洞安装包 链接&am…

最强的表格组件—AG Grid使用以及License Key Crack

PS: 想要官方 License Key翻到最后面 Ag Grid简介 Ag-Grid 是一个高级数据网格&#xff0c;适用于JavaScript/TypeScript应用程序&#xff0c;可以使用React、Angular和Vue等流行框架进行集成。它是一种功能强大、灵活且具有高度可定制性的表格解决方案&#xff0c;提供了丰富…

23款奔驰AMG GT50升级原厂HUD抬头显示系统,增加您的行车安全性

HUD是平视显示器的简称&#xff0c;它原先是运用在航空器上的飞行辅助仪器。指飞行员不需要低头&#xff0c;就能够看到他需要的重要资讯。由于HUD的方便性以及能够提高飞行安全&#xff0c;这项技术后来也发展到汽车行业。汽车搭载的HUD抬头数字显示功能&#xff0c;是利用光学…