【数据结构】之红黑树

news/2024/4/28 0:17:49/文章来源:https://blog.csdn.net/m0_54469145/article/details/131716849

红黑树

  • 红黑树的概念
  • 红黑树的性质
  • 红黑树的插入操作(核心)
    • 情况一:uncle存在且为红
    • 情况二:uncle不存在/存在且为黑(在同一侧)
    • 情况三:uncle不存在/存在且为黑(在两侧)
    • 总结
  • 红黑树的简单实现

红黑树的概念

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

红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(不能有连续红节点)
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?
在这里插入图片描述

最短路径为全黑,最长路径就是红黑节点交替(因为红色节点不能连续),每条路径的黑色节点相同,则最长路径、刚好是最短路径的两倍。

最优情况:全黑或每条路径都是一黑一红的满二叉树,高度logN
最差情况:每颗子树左子树全黑,右子树一黑一红。高度2*logN。

红黑树的插入操作(核心)

为什么新插入的节点必须给红色?
新节点给红色,可能会违反上面说的红黑树性质3;如果新节点给黑色,必定会违反性质4。

根据插入节点后会破坏红黑树的结构,将其分为三种情况

情况一:uncle存在且为红

cur、parent、grandfather都是确定颜色的,uncleu存在且为红
在这里插入图片描述
将p,u改为黑,g改为红,然后把g当成cur,继续向上调整

理解:cur为红那么就需要将parent变为黑;parent变黑需要控制每条路径上黑色节点的数量相同,那么就要把uncle变黑;如果grandfather不是根,需要反转为红,用以控制路径黑节点数量相同。继续向上调整即可

情况二:uncle不存在/存在且为黑(在同一侧)

第一种:uncle不存在,则cur为插入节点,单旋即可
在这里插入图片描述
第二种:uncle存在且为黑,是有第一种情况一变化而来的
在这里插入图片描述

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色–p变黑,g变红

情况三:uncle不存在/存在且为黑(在两侧)

第一种:uncle不存在,则cur为插入节点,左右双旋
在这里插入图片描述
第二种:uncle存在且为黑
在这里插入图片描述

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,则转换成了情况2

总结

插入新节点时,父节点为红,看叔叔的颜色。

  1. 叔叔存在且为红,变色,向上调整(可能变为三种情况中的任意一种)

  2. 叔叔不存在或存在且为黑,同侧。单旋+变色

  3. 叔叔不存在或存在且为黑,异侧,两次单旋+变色

红黑树的简单实现

#pragma once
#include<assert.h>
#include<time.h>
enum Color
{Red,Black,
};
template<class K, class V>
struct RBTreenode
{pair<K, V> _kv;RBTreenode<K, V>* _left;RBTreenode<K, V>* _right;RBTreenode<K, V>* _parent;Color _col;RBTreenode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr),_col(Red){}
};template<class K, class V>
struct RBTree
{typedef RBTreenode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = Black;return true;}//找插入位置Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first>kv.first){parent = cur;cur = cur->_left;}else if(cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else//存在与插入结点相同的结点key{return false;}}cur = new Node(kv);cur->_col = Red;if (parent->_kv.first<kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}//红黑树的调整while (parent&&parent->_col==Red){//祖父节点Node* grandfather = parent->_parent;if (parent==grandfather->_left){Node* uncle = grandfather->_right;//一:uncle存在且为红if (uncle && uncle->_col==Red){parent->_col = uncle->_col = Black;grandfather->_col = Red;//如果祖父节点同时是子节点,考虑双红,需要//继续向上调整cur = grandfather;//考虑parent是否为空parent = cur->_parent;}else{//情况二:cur为红,p红,g黑// uncle存在且为黑/不存在// 祖孙三代全在同一侧//左单选或右单旋if (cur==parent->_left){RotateR(grandfather);parent->_col = Black;grandfather->_col = Red;}else{//情况三:// c,p为红色,g为黑// u不存在/存在且为黑//需要进行双旋操作RotateL(parent);RotateR(grandfather);cur->_col = Black;grandfather->_col = Red;}break;}}else//parent==grandfather->_right{Node* uncle = grandfather->_left;if (uncle&&uncle->_col==Red){parent->_col = uncle->_col = Black;grandfather->_col = Red;cur = grandfather;parent = cur->_parent;}else{//   g                //      p//         cif (cur==parent->_right){RotateL(grandfather);parent->_col = Black;grandfather->_col = Red;}else{//   g                //		   p//    cRotateR(parent);RotateL(grandfather);cur->_col = Black;grandfather->_col = Red;}break;}}}_root->_col = Black;return true;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppNode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppNode == nullptr){_root = subR;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR){subLR->_parent = parent;}Node* ppNode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (ppNode == nullptr){_root = subL;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}void Inorder(){_Inorder(_root);}void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_Inorder(root->_right);}//判断是否有连续红结点和路径上黑色节点数量是否一致bool Check(Node* root,int BlackNum,const int ref){//这里用传值(不用引用)BlackNum,//递归返回上一层的时候BlackNum不会被改变//每个节点都有一个BlackNumif (root==nullptr){if (BlackNum != ref){cout << "违反规则,本条路径结点与基准不一致" << endl;return false;}return true;}if (root->_col==Red && root->_parent->_col==Red){cout << "违反规则:出现连续红结点" << endl;return false;}if (root->_col==Black){++BlackNum;}return Check(root->_left, BlackNum, ref) &&Check(root->_right, BlackNum, ref);}bool IsRBTree(){if (_root==nullptr){return true;}if (_root->_col != Black){return false;}//所有路径黑色节点数相同,以最左侧路径黑色节点数为基准//递归查看是否匹配int ref = 0;Node* left = _root;while (left){if (left->_col==Black){++ref;}left = left->_left;}return Check(_root,0,ref);}
private:Node* _root = nullptr;
};

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

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

相关文章

03插值与拟合

9.已知飞机下轮廓线上数据如下&#xff0c;分别用分段线性插值和三次样条插值求x每改变0.1时的y值。 x035791112131415y01.21.72.02.12.01.81.21.01.6 %9.已知飞机下轮廓线上数据如下&#xff0c;分别用分段线性插值和三次样条插值求每改变0.1时的y值。x [0 3 5 7 9 11 12 1…

简单工厂模式详解

文章目录 前言一、简单工厂模式定义二、举个例子三、简单工厂模式的缺点总结 前言 本篇我们了解一下简单工厂模式&#xff0c;它是设计模式的雏形&#xff0c;是学习设计模式的开端&#xff0c;我会结合案例说明它的设计思路。 一、简单工厂模式定义 简单工厂模式并不是GoF23…

【运维工程师学习五】数据库之MariaDB

【运维工程师学习五】数据库 1、常用的关系型数据库2、C/S结构3、MariaDB图形客户端4、安装MariaDB5、启动MariaDB及验证启动是否成功6、验证启动——端口7、验证启动——进程8、MariaDB配置文件路径主配置文件解读&#xff1a; 9、MariaDB的配置选项10、MariaDB客户端连接1、在…

华为云子网路由表作用及价值

子网路由表 子网路由表作用云专线、VPN的配置与子网路由表强关联&#xff0c;本质是在相应的子网路由表中添加了一条路由Nat路由表问题地址变更问题snat和dnat 子网路由表作用 子网内部作为一个二层网络&#xff0c;通过mac地址互通&#xff0c;不通过路由互通。跨子网&#x…

微信小程序安装和使用 Vant Weapp 组件库

微信小程序安装和使用 Vant Weapp 组件库 1. Vant Weapp 介绍2. Vant Weapp 的 安装2.1. 通过npm安装2.2. 构建npm2.3. 修改 app.json2.4. 修改 project.congfig.json2.5. 测试一下&#xff0c;使用Vant Weapp提供的组件 1. Vant Weapp 介绍 Vant 是一个轻量、可靠的移动端组件…

Three.js环境光,平行光,点光源,聚光灯的创建和灯光辅助线的使用

Three.js中的灯光API使用 1.环境光&#xff08;AmbientLight&#xff09;2.平行光&#xff08;directionalLight&#xff09;3.PointLight(点光源) 4.聚光灯&#xff08;SpotLight&#xff09;5.材质平面&#xff08;PlaneGeometry&#xff09;用于接收&#xff08;平行光和聚…

JavaWeb项目【SpringBoot】——图书项目4.0【源码】:SpringBoot版本 springboot相关技术 项目应用

目录 项目简介思考 & 改进1.Jsp都是同步请求---->改成异步Ajax【完成】2.前端用Jsp技术落后----->用Vue框架【完成】3.架构问题&#xff1a;配置数据和Java代码耦合【完成】3.SQL语句和Java代码耦合【完成】4.架构问题&#xff1a;servlet只能处理一个请求5.响应方式…

[论文分享]MR-MAE:重构前的模拟:用特征模拟增强屏蔽自动编码器

论文题目&#xff1a;Mimic before Reconstruct: Enhancing Masked Autoencoders with Feature Mimicking 论文地址&#xff1a;https://arxiv.org/abs/2303.05475 代码地址&#xff1a;https://github.com/Alpha-VL/ConvMAE&#xff08;好像并未更新为MR-MAE模型&#xff09; …

从Vue2到Vue3【零】——Vue3简介及创建

系列文章目录 内容链接从Vue2到Vue3【零】Vue3简介及创建 文章目录 系列文章目录前言一、Vue3的发布带来了什么1.1 性能提升1.2 源码升级1.3 支持TypeScript1.4 新特性 二、创建Vue3.0工程2.1 什么是Vite2.2 利用Vite创建Vue3.0工程2.3 利用vue-cli脚手架创建Vue3.0工程 三、 …

美团JVM面试题

1. 请解释一下对象创建的过程? Java对象创建的过程主要分为以下五个步骤&#xff1a; 类加载检查 Java虚拟机在读取一条new指令时候&#xff0c;首先检查能否在常量池中定位到这个类的符号引用&#xff0c;并且检查这个符号引用代表的类是否被加载、解析和初始化。如果没有&a…

C#开发的OpenRA游戏之维修按钮

C#开发的OpenRA游戏之维修按钮 前面分析物品的变卖按钮,如果理解这个流程,再看其它按钮的流程,其实是一样的,所以前面的文章是关键,只有理解通透的基础之上,才能继续往下。 维修按钮的存在价值,就是当建筑物受到敌方破坏,还没有完全倒掉之前,可以使用金币来进行修理。…

快速排序的非递归实现、归并排序的递归和非递归实现、基数排序、排序算法的时间复杂度

文章目录 快速排序的非递归三数取中法选取key快速排序三路划分 归并排序的递归归并排序的非递归计数排序稳定性排序算法的时间复杂度 快速排序的非递归 我们使用一个栈来模拟函数的递归过程&#xff0c;这里就是在利用栈分区间。把一个区间分为 [left,keyi-1][key][keyi1,right…

Android 进程与进程之间的通信--AIDL详细教程,以传递对象为例,两个app实现

我这里案例是 通过 IPC 传递对象 &#xff08;以DemoBean类为例&#xff09; 如下&#xff1a; AIDL 使用一种简单语法&#xff0c;允许您通过一个或多个方法&#xff08;可接收参数和返回值&#xff09;来声明接口。参数和返回值可为任意类型&#xff0c;甚至是 AIDL 生成的其…

如何将jar 包下载到自定义maven仓库

下载命令 mvn install:install-file -Dfileartifactid-version.jar -DgroupIdgroupid -DartifactIdartifactid -Dversionversion -Dpackagingjar -DlocalRepositoryPath. -DcreateChecksumtrue参数解释 在上述命令中&#xff0c;需要替换以下参数&#xff1a; artifactid-vers…

计算机组成原理课程设计 报告

在我的博客查看&#xff1a;https://chenhaotian.top/study/computer-composition-principles-course-design/ 计算机组成原理课程设计 报告 一、目的和要求 深入了解计算机各种指令的执行过程&#xff0c;以及控制器的组成&#xff0c;指令系统微程序设计的具体知识&#xf…

【前端知识】React 基础巩固(二十六)——Portals 的使用

React 基础巩固(二十六)——Portals 的使用 Portals 通常&#xff0c;组件会渲染到 root 节点下。可使用 Portals 将组件渲染至其他节点。 添加 id 为 more、modal 的 div 元素 <div id"root"></div> <div id"more"></div> &l…

工作:三菱PLC之CC-Link IE Field Network通讯知识及应用

工作&#xff1a;三菱PLC之CC-Link IE Field Network通讯知识及应用 一、理论 1. 简介连接 CC-LINK-IE通讯分别有 CC-Link IE TSN&#xff0c;CC-Link IE Control Network&#xff0c;CC-Link IE Field Network&#xff0c;CC-Link IE Field Network Basic几种形式&#xff…

成功解决wget下载报错 : wget HTTP request sent, awaiting response... 403 Forbidden

成功解决wget下载报错 : wget HTTP request sent, awaiting response... 403 Forbidden 问题描述解决方案原理什么是User Agent解决 问题描述 –2023-07-15 02:32:57-- https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2023.03-Linux-x86_64.sh Resolving mi…

PyTorch: 池化-线性-激活函数层

文章和代码已经归档至【Github仓库&#xff1a;https://github.com/timerring/dive-into-AI 】或者公众号【AIShareLab】回复 pytorch教程 也可获取。 文章目录 nn网络层-池化-线性-激活函数层池化层最大池化&#xff1a;nn.MaxPool2d()nn.AvgPool2d()nn.MaxUnpool2d()线性层激…

linux 下如何安装 tar.gz包

linux 下如何安装 tar.gz包 解压缩进入解压后的文件目录下 解压缩 tar -zxvf pycharm-community-2023.1.3.tar.gz进入解压后的文件目录下 ./pycharm.sh可执行Pycharm 建议将目录转移到其他位置 我习惯使用2020版本的 下载地址