【C++高阶(四)】红黑树深度剖析--手撕红黑树!

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C++从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++
  🔝🔝


在这里插入图片描述

红黑树

  • 1. 前言
  • 2. 红黑树的概念以及性质
  • 3. 红黑树为什么更实用?
  • 4. 红黑树模拟实现代码框架
  • 5. 红黑树的插入操作初步分析
  • 6. 红黑树的插入操作详解(一)
  • 7. 红黑树的插入操作详解(二)
  • 8. 红黑树的插入代码实现
  • 9. 总结以及拓展

1. 前言

如果说发明AVL树的人是天才,那么
发明红黑树的人可以称为天才中的
精英!为什么AVL树这么强大但是没啥
人用呢?就是因为红黑树比你还好!

本章重点:

本篇文章着重讲解红黑树的概念以及
性质,以及为了维护红黑树这种性质而
做的限制条件.最后模拟实现红黑树的
插入,带大家熟悉变色和旋转规则!


2. 红黑树的概念以及性质

红黑树的概念:

  • 首先红黑树是一颗二叉搜索树
  • 每个节点都有颜色,红色或黑色
  • 最长路径最多是最短路径的二倍

在这里插入图片描述

红黑树的性质:

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的
    则它的两个孩子结点是黑色的
  4. 每条路径上的黑色节点数相同
  5. 每个叶子结点都是黑色的
    (此处的叶子结点指的是空结点)
为啥满足了以上性质的红黑树就一定
能做到最长路径最多是最短路径的二倍?
下面我画一个极限情况来分析一下!

在这里插入图片描述


3. 红黑树为什么更实用?

现在将AVL数和红黑树做一个对比:

  • AVL树阐析:

AVL树是一颗高度平衡二叉树,
它的高度差不能大于1,所以AVL
树的查找是妥妥的O(logn),但是
由于AVL树严格的标准,使得在使用
AVL树时会经常旋转,反而增加了时间!

  • 红黑树阐析:

红黑树是一颗接近平衡的二叉树
它最长路径最多是最短路径的二倍
所以查找的效率是:logn~2logn,
然而2
logn和logn是一个量级的,
并且红黑树的规则没有这么严格,
不会涉及到更多旋转和变色!

综上所述,红黑树的效率虽然比
AVL树差一点,但是总体来说红黑树胜!

4. 红黑树模拟实现代码框架

首先,每个节点都要存一个颜色,
这里我们使用枚举enum来实现
并且和AVL一样也是三叉链!
请看代码:

enum Colour
{RED,BLACK
};
template<class K,class V>
struct RBTreeNode
{RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv),_col(RED){}RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv; Colour _col;
};template<class K, class V>
struct RBTree
{
typedef RBTreeNode<K, V> Node;
private:Node* _root = nullptr;
};

有了代码框架后,现在来看看插入


5. 红黑树的插入操作初步分析

和AVL树很相似,红黑树的插入
也是分为两步走:

  1. 按照二叉搜索树的规则插入值
  2. 插入后根据颜色或高度做旋转或变色

众所周知啊,第一步很简单
甚至可以将之前的代码抄过来:

bool insert(const pair<K, V>& kv)//第一步:按照二叉搜索树的方式插入值
{if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else return false;}cur = new Node(kv);if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;//此时new出来的节点的parent还指向空cur->_parent = parent;///前面的过程和AVL树一致
}

走到这步后,有个问题,新插入的节点
是选择插入红色还是黑色?

对于这个问题,我们要思考两个点,
一是如果插入的是黑色节点,我们
可能会打破哪个规则?如果是插入
红色节点又可能会打破哪个规则?

  • 插入黑色节点,打破规则四,很难办
    因为每个路径检查起来很难!

  • 插入红色节点,打破规则三,比打破
    四要好一些,因为只用看父亲是否为红

综上所述,插入红色更优!
换句话说,你犯错肯定宁愿犯轻一点
的错误被妈妈打一顿,也不愿意犯很重
的错直接被家族除名了对吧[doge]

6. 红黑树的插入操作详解(一)

如果插入的节点的父亲是黑色节点,
那么正是我们想看见的,不用管它了!

那么如果插入的节点的父亲是红色呢?
很明显,这违反了规则三,有连续的红色
节点,所以此时需要做处理了!

先来看一个最简单的情况:

在这里插入图片描述

其实红黑树插入节点后的变色和
旋转规则主要是看叔叔,叔叔的情况
不同,那么对应的处理手段就不同,这里
只通过简单变色手段就可以满足规则了!
并且在上图中,将爷爷变成红色后可能会
出现问题,因为爷爷的父亲不知道是红色
还是黑色,所以要不断向上做判断

若不向上更新,可能会有这种情况:

在这里插入图片描述


7. 红黑树的插入操作详解(二)

当讲解完最简单的情况后,还剩下两种
情况,这两种情况内部又可细分出几种
情况,请同学们耐心学习!

情况二:cur为红.p为红.g为黑.u不存在/为黑
(并且cur和p都是左或右)

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

p为g的左孩子,cur为p的左孩子,则右单旋
p为g的右孩子,cur为p的右孩子,则左单旋
p,g变色—p变黑,g变红

情况三:cur为红.p为红.g为黑.u不存在/为黑
(并且若p是g的左,cur就是p的右)

在这里插入图片描述

p为g的左孩子,cur为p的右孩子,则做左单旋
p为g的右孩子,cur为p的左孩子,则做右单旋
则转换成了情况二

至此,红黑树的插入的所有情况都
讲解完毕,接下来就是代码实现了!


8. 红黑树的插入代码实现

在整个大情况分类中,可以归为两类
一是叔叔为红色,二是叔叔为黑色或者
叔叔不存在,我们围绕着这两个大方向写!

//走到这一步后,就已经找到cur和parent了!
while (parent && parent->_col == RED)//当parent为黑就不用往上更新了
{if (parent == _root){_root->_col = BLACK;break;}Node* grandf = parent->_parent;assert(grandf);assert(grandf->_col == BLACK);Node* uncle = nullptr;if (parent == grandf->_left)//判断叔叔在par的左还是右uncle = grandf->_right;else uncle = grandf->_left;if (uncle == nullptr || uncle->_col == BLACK)//uncle为空或为黑有四种情况来变色+旋转{if (parent == grandf->_left && cur == parent->_left)//左左->右旋+变色{RotateR(grandf);parent->_col = BLACK;grandf->_col = RED;break;}else if (parent == grandf->_right && cur == parent->_right)//右右->左旋+变色{RotateL(grandf);parent->_col = BLACK;grandf->_col = RED;break;}else if (parent == grandf->_left && cur == parent->_right)//左右->先左旋再右旋再变色{RotateL(parent);RotateR(grandf);cur->_col = BLACK;grandf->_col = RED;break;}else if (parent == grandf->_right && cur == parent->_left)//右左->先右旋再左旋再变色{RotateR(parent);RotateL(grandf);cur->_col = BLACK;grandf->_col = RED;break;}}else if (uncle && uncle->_col == RED)//叔叔为红,直接变色,不旋转{parent->_col = BLACK;uncle->_col = BLACK;grandf->_col = RED;cur = grandf;parent = cur->_parent;//将parent更新后往上传!}_root->_col = BLACK;
}

可以发现一个问题,只要是叔叔的颜色
是黑色或叔叔不存在的情况下,执行完
旋转+变色后都直接break了,这是因为
在这种情况下,父亲节点都被变成了黑色,
也就没必要继续往上了!并且在红黑树的
左旋和右旋中,代码其实和AVL树的旋转是
一模一样的,所以直接copy一份就行了!

若你不清楚旋转的代码,请看这篇文章:

AVL树模拟实现!


9. 总结以及拓展

AVL树和红黑树的代码实现都只讲解
了插入操作,因为删除操作太复杂了,
并且就算实现了删除操作也没有太大
的实际意义,所以只需要了解插入即可!

并不是需要你真正的会手撕!

拓展阅读:

红黑树的删除图解


🔎 下期预告:哈希表,哈希桶 🔍

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

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

相关文章

计算机网络之数据链路层

一、概述 1.1概述 物理层发出去的信号需要通过数据链路层才知道是否到达目的地&#xff1b;才知道比特流的分界线 链路(Link)&#xff1a;从一个结点到相邻结点的一段物理线路&#xff0c;中间没有任何其他交换结点数据链路(Data Link)&#xff1a;把实现通信协议的硬件和软件…

电商API接口|电商数据接入|拼多多平台根据商品ID查商品详情SKU和商品价格参数

随着科技的不断进步&#xff0c;API开发领域也逐渐呈现出蓬勃发展的势头。今天我将向大家介绍API接口&#xff0c;电商API接口具备独特的特点&#xff0c;使得数据获取变得更加高效便捷。 快速获取API数据——优化数据访问速度 传统的数据获取方式可能需要经过多个中介环节&…

华大基因认知障碍基因检测服务,助力认知障碍疾病防控

认知障碍是一种严重的神经系统疾病&#xff0c;对人类的脑健康产生了重大影响。据报告显示&#xff0c;在我国65岁以上的人群中&#xff0c;存在轻度认知障碍的患者约为3,800万&#xff0c;而中重度痴呆患者则约为1,500万&#xff0c;患病人口数量庞大。这种疾病不仅会对患者的…

免费多域名SSL证书

顾名思义&#xff0c;免费多域名SSL证书就是一种能够为多个域名或子域提供HTTPS安全保护的证书。这意味着&#xff0c;如果您有三个域名——例如example.com、example.cn和company.com&#xff0c;您可以使用一个免费的多域名SSL证书为所有这些域名提供安全保障&#xff0c;而无…

TransFusionNet:JetsonTX2下肝肿瘤和血管分割的语义和空间特征融合框架

TransFusionNet: Semantic and Spatial Features Fusion Framework for Liver Tumor and Vessel Segmentation Under JetsonTX2 TransFusionNet&#xff1a;JetsonTX2下肝肿瘤和血管分割的语义和空间特征融合框架背景贡献实验方法Transformer-Based Semantic Feature Extractio…

AI写代码 可以代替人工吗?

近年AI技术非常火热&#xff0c;有人就说&#xff0c;用AI写代码程序员不就都得下岗吗&#xff1f;对此我的回答是否定的&#xff0c;因为AI虽然已经有了编写代码的能力&#xff0c;但它现在的水平大多还仅限于根据业务需求搭建框架&#xff0c;而具体的功能实现还尚且稚嫩&…

【愚公系列】保姆级教程带你实现HarmonyOS手语猜一猜元服务

&#x1f680;前言 最近HarmonyOS NEXT大火&#xff0c;这个纯血鸿蒙吸引力了大家的关注。虽然现在还没面向个人开发者开放&#xff0c;但我们可以基于最新的API9及开发工具来尝试开发鸿蒙新的应用形态——元服务。来体验下未来在HarmonyOS NEXT上实现的应用开发。 HarmonyOS…

什么是高防IP?有什么优势?怎么选择高防IP?

在当今的互联网环境中&#xff0c;分布式拒绝服务&#xff08;DDoS&#xff09;攻击已经成为一种常见的安全威胁。这种攻击通过向目标服务器发送大量的无效流量&#xff0c;使其无法处理正常的请求&#xff0c;从而达到迫使服务中断的目的。作为一个用户&#xff0c;你是否曾遇…

QGIS文章五——对遥感影像进行土地类型分类—监督分类(dzetsaka : classification tool)...

dzetsaka classification tool是QGIS的强大分类插件&#xff0c;目前主要提供了高斯混合模型分类器、Random Forest、KNN和SVM四种分类器模型&#xff0c;相比于SCP(Semi-Automatic Classification)&#xff0c;他的一个特点就是功能专一&#xff0c;操作简单。 从十一月开始一…

Linux基础命令3

移动&#xff0c;剪切文件 普通文件的移动剪切 现在在这儿 上图中&#xff0c;mv y.x ./tmp的意思&#xff0c;就是将当前路径下的y.x文件进行剪切&#xff0c;然后放到路径为当前路径下的tmp目录文件夹里面 操作完成后可以cd tmp&#xff0c;ls看到y.x文件已经在里面了 现在…

京东内部员工,爆料工资与公积金收入!

精彩回顾&#xff1a;进了央企&#xff0c;拿了户口&#xff0c;却感觉被困住了。 每个企业都有它的一套规则&#xff0c;哪些人适合加薪&#xff0c;哪些人适合拿奖金&#xff0c;哪些人适合给股票期权等等。但是说实话&#xff0c;很多人都只能拿底薪&#xff0c;这些福利啥的…

数据挖掘 K近邻

什么时候用K近邻&#xff1f; 交叉验证的时候。最常见的交叉验证方法是K折交叉验证&#xff0c;其中数据集被均匀分成K个子集&#xff0c;称为折&#xff0c;然后执行K次训练和测试&#xff0c;每次选择不同的折作为测试集&#xff0c;其余的作为训练集。最后&#xff0c;将K次…

JavaScript编程基础 – 对象

JavaScript编程基础 – 对象 JavaScript Programming Essentials – Object 本文简要介绍JavaScript面向对象编程&#xff0c;如何实现其中的对象以及实例演示&#xff0c;希望对大家学习JavaScript有所帮助。 1. 面向对象编程特点 面向对象编程(Object-Oriented Programmi…

浅谈JDK动态代理(上)

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 到目前为止&#xff0c…

力扣OJ题讲解——循环队列

今天我们一起来做一道关于队列的OJ题目&#xff0c;这是力扣题目622题&#xff0c;点击题目链接可以直接跳转&#xff0c;https://leetcode.cn/problems/design-circular-queue/ 首先&#xff0c;我们看到要求&#xff0c;需要我们实现哪些功能&#xff1f; 我们需要设置队列长…

经典双指针算法试题(二)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、有效三角形的个数1、题目讲解2、讲解算法原理3、代码实现 二、查找总价格为目标值的两个商…

Hutool

一、简介 Hutool是一个小而全的Java工具类库&#xff0c;通过静态方法封装&#xff0c;降低相关API的学习成本&#xff0c;提高工作效率&#xff0c;使Java拥有函数式语言般的优雅 官方文档: https://www.hutool.cn/docs/#/ 二、包含组件 一个Java基础工具类&#xff0c;对文…

allegro画封装时使用坐标指令无效

使用坐标指令时显示&#xff1a;“Pick is outside the extent of the drawing…pick again” 这是因为你放的引脚已经超出你这个绘制界面的定义尺寸&#xff0c;需要到Setup->Design pararmeters…里面去将图幅改大一点&#xff0c;如下图所示&#xff1a; 然后点击Design…

消息中间件——RabbitMQ(三)理解RabbitMQ核心概念和AMQP协议!

前言 本章学习&#xff0c;我们可以了解到以下知识点&#xff1a; 互联网大厂为什么选择RabbitMQ&#xff1f;RabbiMQ的高性能之道是如何做到的&#xff1f;什么是AMQP高级协议&#xff1f;AMQP核心概念是什么&#xff1f;RabbitMQ整体架构模型是什么样子的&#xff1f;Rabbi…

P8599 [蓝桥杯 2013 省 B] 带分数(dfs+全排列+断点判断)

思路&#xff1a;1.深度枚举所有排列情况 2.设置为每个排列设置两个断点&#xff0c;分为三部分&#xff1a;a,b,c 3.转换为乘法判断条件&#xff0c;满足加一 代码如下&#xff1a;&#xff08;可用next_permutation全排列函数代替dfs&#xff09; #include<iostream>…