[数据结构]红黑树,详细图解插入

目录

一、红黑树的概念

二、红黑树的性质

三、红黑树节点的定义

四、红黑树的插入(步骤)

1.为什么新插入的节点必须给红色?

2、插入红色节点后,判定红黑树性质是否被破坏

五、插入出现连续红节点情况分析+图解(看uncle节点)

5.1、uncle存在且为红

5.2、uncle不存在

1、单旋

2、双旋

5.3、uncle存在且为黑

1、单旋

2、双旋

六、插入总结

1、红黑树插入的两种步骤

 2、插入代码

七、红黑树总结及代码


一、红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制

红黑树确保——没有一条路径会比其他路径长出两倍,因而是接近平衡的


二、红黑树的性质

1. 每个结点不是红色就是黑色

2. 根节点是黑色的 

3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 (没有连续的红节点

4. 从任一结点到其所有后代叶结点的简单路径上,均包含相同数目的黑结点 

5. 每个叶子结点都是黑色的(此处的叶子结点指的是NIL空结点)

        最优情况:全黑或每条路径都是一黑一红的满二叉树,高度logN

        最差情况:每颗子树左子树全黑,右子树一黑一红。高度2*logN。

        可以发现,最坏情况的时间复杂度和AVL树一样,都是O(logN),但是红黑树这种近似平衡的结构减少了大量旋转,综合性能优于AVL树。


三、红黑树节点的定义

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

四、红黑树的插入(步骤)

1.为什么新插入的节点必须给红色?

(1)新节点给红色,可能出现连续红节点

(2)如果新节点给黑色,必定会违反性质4(其每条路径的黑色节点数量相同

2、插入红色节点后,判定红黑树性质是否被破坏

因为新节点的默认颜色是红色,所以

(1)双亲节点的颜色是黑色,没有违反红黑树任何 性质,则不需要调整;

(2)双亲节点为红色,就会出现连续的红节点,此时需要对红黑树分情况来讨论:见下一部分


五、插入出现连续红节点情况分析+图解(看uncle节点)

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

下面的分析都是以p为g的左孩子为例

5.1、uncle存在且为红

cur插入后,p和u变黑,g变红

(1)g没有父亲,g为根,g变黑

(2)g有父亲。其为黑,结束;其为红,后把g当成cur,继续向上调整

5.2、uncle不存在

u不存在,则cur一定是新插入的节点

(如果cur不是新插入的节点,则cur和p一定有一个节点是黑色,否则每条路径黑色节点不相同

下图为解释:

1、单旋

右单旋

2、双旋

 左单旋 + 右单旋 

5.3、uncle存在且为黑

uncle存在且为黑,是情况一变来的,所以cur原来的节点一定是黑色的

现在其是红色的原因是,cur的子树在调整过程中将cur的颜色由黑变红。

1、单旋

右单旋

2、双旋

左单旋 + 右单旋

六、插入总结

1、红黑树插入的两种步骤

1、uncle存在且为红

2、uncle不存在 或者 uncle存在且为黑

通过分析,

uncle不存在的单旋 和 uncle存在且为黑的单旋 可以写在一起,

uncle不存在的双旋 和 uncle存在且为黑的双旋 可以写在一起,

不论uncle存在或者不存在,都不影响此步的单旋或者双旋

当p为g的右孩子时,操作都相反。

详细步骤见其中while (parent && parent->_col == RED)这一步。

 2、插入代码

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->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//p为g左孩子if (parent == grandfather->_left){Node* uncle = grandfather->_right;// 情况1:u存在且为红 if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // u不存在 或 存在且为黑{//情况2.1 , 3.1if (cur == parent->_left){//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//情况2.2 , 3.2{//     g//   p//		cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}//p为g右孩子else // parent == grandfather->_right{Node* uncle = grandfather->_left;// u存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){// g//	  p//       cRotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{// g//	  p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}

七、红黑树总结及代码

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(logN),红黑树不追求绝对平衡,只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数, 所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

using namespace std;enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _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->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//p为g左孩子if (parent == grandfather->_left){Node* uncle = grandfather->_right;// 情况1:u存在且为红 if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // u不存在 或 存在且为黑{//情况2.1 , 3.1if (cur == parent->_left){//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//情况2.2 , 3.2{//     g//   p//		cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}//p为g右孩子else // parent == grandfather->_right{Node* uncle = grandfather->_left;// u存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){// g//	  p//       cRotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{// g//	  p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateL(Node* parent){++_rotateCount;Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}void RotateR(Node* parent){++_rotateCount;Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright)curright->_parent = parent;Node* ppnode = parent->_parent;cur->_right = parent;parent->_parent = cur;if (ppnode == nullptr){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}bool CheckColour(Node* root, int blacknum, int benchmark){if (root == nullptr){if (blacknum != benchmark)return false;return true;}if (root->_col == BLACK){++blacknum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_kv.first << "出现连续红色节点" << endl;return false;}return CheckColour(root->_left, blacknum, benchmark)&& CheckColour(root->_right, blacknum, benchmark);}bool IsBalance(){return IsBalance(_root);}bool IsBalance(Node* root){if (root == nullptr)return true;if (root->_col != BLACK){return false;}// 基准值int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)++benchmark;cur = cur->_left;}return CheckColour(root, 0, benchmark);}int Height(){return Height(_root);}int Height(Node* root){if (root == nullptr)return 0;int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}private:Node* _root = nullptr;public:int _rotateCount = 0;
};

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

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

相关文章

STM32 HAL库USART串口DMA IDLE中断编程:避坑指南

HAL_UART_Receive接收最容易丢数据了,STM32 HAL库UART查询方式实例 可以考虑用中断来实现,但是HAL_UART_Receive_IT还不能直接用,容易数据丢失,实际工作中不会这样用,STM32 HAL库USART串口中断编程&#xff1a;演示数据丢失, 需要在此基础优化一下. STM32F103 HAL库USART串口…

sql注入中information_schema被过滤的问题

目录 一、information_schema库的作用 二、获得表名 2.1 sys.schema_auto_increment_columns 2.2 schema_table_statistics 三、获得列名 join … using … order by盲注 子查询 在进行sql注入时&#xff0c;我们经常会使用information_schema来进行爆数据库名、表名、…

Jenkins 给任务分配 节点(Node)、设置工作空间目录

Jenkins 给任务分配 节点(Node)、设置工作空间目录 创建 Freestyle project 类型 任务 任务配置 Node 打开任务-> Configure-> General 勾选 Restrict where this project can be run Label Expression 填写一个 Node 的 Label&#xff0c;输入有效的 Label名字&#x…

Electron:使用electron-react-boilerplate创建一个react + electron的项目

使用 electron-react-boilerplate git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name cd your-project-name npm install npm start 安装不成功 在根目录加上 .npmrc文件 内容为 electron_…

数控机床设备分布式健康监测与智能维护系统MTAgent

数控机床设备分布式健康监测与智能维护系统MTAgent-v1.1融合了目前各种先进的信号处理以及信息分析算法以算法工具箱的方式&#xff0c;采用了一种开发的、模块化的结构实现信号各种分析处理&#xff0c;采用Python编程语言&#xff0c;满足不同平台需求(包括Windows、Linux)。…

FPGA VIVADO:axi-lite 从机和主机

FPGA VIVADO:axi-lite 从机和主机 TOC在这里插入代码片 前言 协议就不详细讲解了&#xff0c;直接看手册即可。下面主要如何写代码和关键的时序。 此外下面的代码可以直接用于实际工程 一、AXI-LITE 主机 数据转axi lite接口&#xff1a; 读/写数据FIFO缓存 仲裁&#xff1a…

1. 对比 LVS 负载均衡群集的 NAT 模式和 DR 模式,比较其各自的优势 。2. 基于 openEuler 构建 LVS-DR 群集。

DR 模式 * 负载各节点服务器通过本地网络连接&#xff0c;不需要建立专用的IP隧道 原理&#xff1a;首先负载均衡器接收到客户的请求数据包时&#xff0c;根据调度算法决定将请求发送给哪个后端的真实服务器&#xff08;RS&#xff09;。然后负载均衡器就把客户端发送的请求数…

ollama server启动服务后如何停止

要停止 Ollama 服务器服务&#xff0c;取决于如何启动该服务的。以下是几种常见的启动方法和相应的停止服务的步骤&#xff1a; 1. 直接在命令行中启动 如果是在命令行中直接启动 Ollama 服务器的&#xff0c;例如使用以下命令&#xff1a; ollama serve 可以通过以下方式停…

【设计模式】03-理解常见设计模式-行为型模式(专栏完结)

前言 前面我们介绍完创建型模式和创建型模式&#xff0c;这篇介绍最后的行为型模式&#xff0c;也是【设计模式】专栏的最后一篇。 一、概述 行为型模式主要用于处理对象之间的交互和职责分配&#xff0c;以实现更灵活的行为和更好的协作。 二、常见的行为型模式 1、观察者模…

mapbox基础,使用geojson加载line线图层,实现纯色填充、图片填充、虚线和渐变效果

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️line线图层样式二、🍀使用geojson加载…

深入浅出:CUDA是什么,如何利用它进行高效并行计算

在当今这个数据驱动的时代&#xff0c;计算能力的需求日益增加&#xff0c;特别是在深度学习、科学计算和图像处理等领域。为了满足这些需求&#xff0c;NVIDIA推出了CUDA&#xff08;Compute Unified Device Architecture&#xff09;&#xff0c;这是一种并行计算平台和编程模…

LNMP+Zabbix安装部署(Zabbix6.0 Lnmp+Zabbix Installation and Deployment)

LNMPZabbix安装部署&#xff08;Zabbix6.0&#xff09; 简介 LNMP&#xff08;Linux Nginx MySQL PHP&#xff09;是一种流行的Web服务器架构&#xff0c;广泛用于搭建高性能的网站和应用程序。Zabbix 是一个开源的监控软件&#xff0c;可以用来监控网络、服务器和应用程序…

Docker 部署 Dify:轻松集成 Ollama 和 DeepSeek

1 Ollama的安装及使用 1.1 什么是Ollama&#xff1f; Ollama 是一个用于本地部署和运行大型语言模型的框架。 Ollama 的作用包括&#xff1a; 本地模型运行&#xff1a;Ollama 允许在本地机器上运行大型语言模型&#xff08;如 LLaMA、DeepSeek 等&#xff09;&#xff0c;无…

C++笔记之标准库中用于处理迭代器的`std::advance`和`std::distance`

C++笔记之标准库中用于处理迭代器的std::advance和std::distance code review! 文章目录 C++笔记之标准库中用于处理迭代器的`std::advance`和`std::distance`一.`std::advance`函数原型参数说明使用场景示例代码示例 1:移动 `std::vector` 的随机访问迭代器示例 2:移动 `st…

工业制造能耗管理新突破,漫途MTIC-ECM平台助力企业绿色转型!

在工业制造领域&#xff0c;能源消耗一直是企业运营成本的重要组成部分。随着“双碳”目标的推进&#xff0c;如何实现高效能耗管理&#xff0c;成为制造企业亟待解决的问题。漫途MTIC-ECM能源能耗在线监测平台&#xff0c;结合其自研的硬件产品&#xff0c;为工业制造企业提供…

C语言——深入理解指针(2)(数组与指针)

文章目录 数组名的理解使用指针访问数组一维数组传参的本质冒泡排序二级指针指针数组指针数组模拟二维数组 数组名的理解 之前我们在使用指针访问数组内容时&#xff0c;有这样的代码&#xff1a; int arr[10]{1,2,3,4,5,6,7,8,9,10}; int* p&arr[0];这里我们使用&ar…

在Windows系统中安装Open WebUI并连接Ollama

Open WebUI是一个开源的大语言模型&#xff08;LLM&#xff09;交互界面&#xff0c;支持本地部署与离线运行。通过它&#xff0c;用户可以在类似ChatGPT的网页界面中&#xff0c;直接操作本地运行的Ollama等大语言模型工具。 安装前的核心要求&#xff1a; Python 3.11&#…

Day4:强化学习之Qlearning走迷宫

一、迷宫游戏 1.环境已知 迷宫环境是定义好的&#xff0c;障碍物位置和空位置是已知的&#xff1b; # 定义迷宫 grid [[0, 0, 0, 1, 0],[0, 1, 0, 1, 0],[0, 1, 0, 0, 0],[0, 0, 0, 1, 0],[0, 1, 1, 1, 0] ] 2.奖励方式已知 如果碰到障碍物则得-1&#xff0c;如果到终点则…

家里WiFi信号穿墙后信号太差怎么处理?

一、首先在调制解调器&#xff08;俗称&#xff1a;猫&#xff09;测试网速&#xff0c;网速达不到联系运营商&#xff1b; 二、网线影响不大&#xff0c;5类网线跑500M完全没问题&#xff1b; 三、可以在卧室增加辅助路由器&#xff08;例如小米AX系列&#xff09;90~200元区…

视点开场动画实现(九)

这个相对比较简单&#xff1a; void COSGObject::FlyTo(double lon, double lat, double hei) {theApp.bNeedModify TRUE;while(!theApp.bCanModify)Sleep(1);em->setViewpoint(osgEarth::Viewpoint("0",lon, lat, 0, 0, -45, hei), 2);theApp.bNeedModify FAL…