STL容器之map和set的补充红黑树

news2025/1/23 21:23:06

三、红黑树

​ 红黑树比起avl树是哟啊更优一点的。

3.1概念

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

3.2性质

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

2.根节点是黑色的;

3.如果一个节点是红色的,则它的两个孩子结点都必须是黑色的 ,即父子不能同时为红,可为黑;

4.对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点;

5.所有NIL叶子节点都是黑色的,NIL为空节点;

总结一下,最短路径就是全黑,而最长路径就是一黑一红相间;黑色节点的占比范围为[50%,100%],50%时黑红数量相同,100%时全黑;

​ 优点:avl树需要严格的平衡,有着大量的旋转,而红黑树只需要满足最长路径不超过最短路径的二倍,是不严格的平衡,只需要局部子树没有连续的红节点并且子树的每条路径的黑节点数目相同,所以旋转就相对较少,尽管时间复杂度是avl树的2倍,但是减少了旋转的效率,总的来说是提高了效率。

3.3RB树的模拟实现

3.3.1插入原理

​ 先实现二叉搜索树,之后插入会遇到的情况:

​ 1.插入时选择插入红色,因为黑色需要每一个路径都进行更新,而红色只需要调整这一个路,不会影响其他路径;

​ 2.如果父节点是黑的,就不需要调整了,如果是红的就需要修改,如果父节点是红色的那么他一定不是根节点,根节点是没有爷爷的,而且爷爷一定是黑色的;

​ 3.红黑树要看的是叔叔即父节点的兄弟节点,如果叔叔存在且是红色,那么父亲和叔叔变黑,爷爷变红,这样就保证了父亲和叔叔这两条路径一定是黑色节点数保持不变,且连续的红节点问题暂时的解决了,仅仅解决的是局部的问题,还需要继续向上解决问题。

​ 4.继续向上处理分为三种情况:1.爷爷就是根节点,没有父亲了,只需要将爷爷再变黑即可;2.有父亲,但是父亲是黑色,直接结束;3.有父亲,父亲是红色,重复上述过程,cur=grandfather,parent=cur->parent,grandfather=parnet->parent;

​ 5.当黑色节点多时,插入红色节点就方便了。

​ 6.对于叔叔如果不存在,旋转加变色;叔叔存在为红色,父亲叔叔变黑,爷爷变红;如果叔叔存在为黑色,旋转加变色;

总结:

​ 1.如果父亲是黑色,就停止了,父亲是根,父亲变黑停止;

​ 2.剩下的需要调整的情况一定是,cur与parent是红色的,grandfather是黑色的,否则就是出错了;

​ 3.接下来就需要讨论uncle,如果uncle存在且为红,p与u变为黑,g变为红,g变成cur继续向上调整,重复1、2;

在这里插入图片描述

​ 如果cur为新增的节点abcde为空,就是cur为红不断地向上更新;如果cde为每条路径只有一个黑节点的红黑树,每个有四种情况如下,此时cur为黑色,a和b节点都是红色且没有子节点,选择在ab位置插入4种,一共有4*4*4*4=256种可能;

在这里插入图片描述

​ 如果如果cde为每条路径只有两个黑节点的红黑树,则需要变2层才能结束;

​ 4.如果uncle存在且为黑或者uncle不存在,uncle为黑色,cur不可能为红色新增,一定为黑色,且有两个红节点;

在这里插入图片描述

c是每条路径包含是一个黑色节点的红黑树,de是红节点或者为空;4*2*2*4=64种;

#pragma once
#include <cassert>
namespace RbTree
{
	enum Colour
	{
		RED,
		BLACK,
	};
	template <class K, class V>
	struct RBTreeNode
	{
		RBTreeNode(const pair<K, V>& kv) : kv_(kv), left_(nullptr), right_(nullptr), parent_(nullptr), col_(BLACK) {}
		pair<K, V> kv_;
		RBTreeNode<K, V>* left_;
		RBTreeNode<K, V>* right_;
		RBTreeNode<K, V>* parent_;
		Colour col_;
	};

	template <class K, class V>
	class RBTree
	{
		typedef RBTreeNode<K, V> node;
		
	public:
        //普通成员函数
		bool insert(const pair<K, V>& kv);
		void rotatel(node* parent);
		void rotater(node* parent);
		void rotaterl(node* parent);
		void rotatelr(node* parent);
		void inorder()
		{
			_inorder(root_);
		}
		bool isrbtree()
		{
			return _isrbtree(root_);
		}
		bool checkcolour(node* root, int blacknum, int reference);
	private:
        //私有成员
		void _inorder(node* root);
		bool _isrbtree(node* root);
		node* root_ = nullptr;
	};
	template <class K, class V>
	void RBTree<K, V>::rotatel(node* parent)
	{
		node* cur = parent->right_;
		node* curleft = cur->left_;
		parent->right_ = curleft;
		node* ppnode = parent->parent_;
		if (curleft)
			curleft->parent_ = parent;
		parent->parent_ = cur;
		cur->left_ = parent;

		if (parent == root_)
		{
			root_ = cur;
			cur->parent_ = nullptr;
		}
		else
		{
			if (ppnode->left_ == parent)
			{
				ppnode->left_ = cur;
			}
			else
			{
				ppnode->right_ = cur;
			}
			cur->parent_ = ppnode;
		}
	}
	template <class K, class V>
	void RBTree<K, V>::rotater(node* parent)
	{
		node* cur = parent->left_;
		node* curright = cur->right_;
		node* ppnode = parent->parent_;
		parent->left_ = curright;
		cur->right_ = parent;
		if (curright)
			curright->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;
		}
	}
	template <class K, class V>
	void RBTree<K, V>::rotaterl(node* parent)
	{
		rotater(parent->right_);
		rotatel(parent);
	}
	template <class K, class V>
	void RBTree<K, V>::rotatelr(node* parent)
	{
		rotatel(parent->left_);
		rotater(parent);
	}
	template <class K, class V>
	bool RBTree<K, V>::insert(const pair<K, V>& kv)
	{
		if (root_ == nullptr)
		{
			root_ = new node(kv);
			root_->col_ = BLACK;
			return true;
		}
		node* cur = root_;
		node* parent = cur;
		while (cur)
		{
			if (kv.first > cur->kv_.first)
			{
				parent = cur;
				cur = cur->right_;
			}
			else if (kv.first < cur->kv_.first)
			{
				parent = cur;
				cur = cur->left_;
			}
			else
			{
				return false;
			}
		}
		cur = new node(kv);
		cur->col_ = RED;
		if (kv.first > parent->kv_.first)
		{
			parent->right_ = cur;
		}
		else
		{
			parent->left_ = cur;
		}
		cur->parent_ = parent;
		while (parent && parent->col_ == RED)
		{
			node* grandfather = parent->parent_;
			if (grandfather->left_ == parent)
			{
				node* uncle = grandfather->right_;
				// 叔叔存在且为红
				if (uncle && uncle->col_ == RED)
				{
					// 变色+向上处理
					parent->col_ = uncle->col_ = BLACK;
					grandfather->col_ = RED;
					// 继续向上处理
					cur = grandfather;
					parent = cur->parent_;
				}
				else
				{
					// 叔叔不存在或者叔叔存在且为黑
					if (cur == parent->left_)
					{
						// 右单旋
						rotater(grandfather);
						parent->col_ = BLACK;
						grandfather->col_ = RED;
					}
					else
					{
						// 左右双旋
						rotatelr(grandfather);
						cur->col_ = BLACK;
						grandfather->col_ = RED;
					}
					break;
				}
			}
			else if (grandfather->right_ == parent)
			{
				node* uncle = grandfather->left_;
				// 叔叔存在且为红
				if (uncle && uncle->col_ == RED)
				{
					// 变色+向上处理
					parent->col_ = uncle->col_ = BLACK;
					grandfather->col_ = RED;
					// 继续向上处理
				}
				else
				{
					// 叔叔不存在或者叔叔存在且为黑
					if (cur == parent->right_)
					{
						// 左单旋
						rotatel(grandfather);
						parent->col_ = BLACK;
						grandfather->col_ = RED;
					}
					else
					{
						// 右左双旋
						rotaterl(grandfather);
						cur->col_ = BLACK;
						grandfather->col_ = RED;
					}
					break;
				}
			}
			else
			{
				assert(false);
			}
		}
		// 如果父亲不存在,说明cur为根,就要将cur置为黑色,如果父亲存在且为黑色则不需要处理;
		root_->col_ = BLACK;
		return true;
	}
	template <class K, class V>
	void RBTree<K, V>::_inorder(node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_inorder(root->left_);
		cout << root->kv_.first << ":" << root->kv_.second << endl;
		_inorder(root->right_);
	}
	template<class K, class V>
	bool RBTree<K, V>::checkcolour(node* root, int blacknum, int reference)
	{
		if (root == nullptr)
		{
			if (blacknum != reference)
			{
				return false;
			}
			return true;
		}
		if (root->col_ == BLACK)
		{
			blacknum++;
		}
		if (root->col_ == RED && root->parent_->col_ == RED)
		{
			cout << root->kv_.first << "出现连续红色节点" << endl;
			return false;
		}
		return checkcolour(root->left_, blacknum, reference) && checkcolour(root->right_, blacknum, reference);
	}
	template<class K, class V>
	bool RBTree<K, V>::_isrbtree(node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		if (root->col_ != BLACK)
		{
			return false;
		}
		int reference = 0;
		node* cur = root_;
		while (cur)
		{
			if (cur->col_ == BLACK)
			{
				reference++;
			}
			cur = cur->left_;
		}
		return checkcolour(root, 0, reference);//检查连续的红色
	}
}

3.4红黑树的应用

1.C++ STL库 – map/set、mutil_map/mutil_set;

2.Java 库;

3.linux内核;

4.其他一些库;

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

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

相关文章

DataFunSummit 2023:洞察现代数据栈技术的创新与发展(附大会核心PPT下载)

随着数字化浪潮的推进&#xff0c;数据已成为企业竞争的核心要素。为了应对日益增长的数据挑战&#xff0c;现代数据栈技术日益受到业界的关注。DataFunSummit 2023年现代数据栈技术峰会正是在这样的背景下应运而生&#xff0c;汇聚了全球数据领域的精英&#xff0c;共同探讨现…

华为数通方向HCIP-DataCom H12-821题库(多选题:101-120)

第101题 下面关于Network-Summary-LSA描述正确的是 A、Network-Summary-LSA中的Metric被设置成从该ABR到达目的网段的开销值 B、Network-Summary-LSA中 的Netmask被设置成目的网段的网络掩码 C、Network-Summary-LSA中的Link State ID被设置成目的网络的IP地址 D、Network-Sum…

项目一:踏上Java开发之旅(2023软件1班)

文章目录 一、实战概述二、实战步骤任务1&#xff1a;安装配置JDK开发第一个Java程序1、安装JDK2、配置Java环境变量3、开发第一个Java程序&#xff08;1&#xff09;编写源程序 - HelloWorld.java&#xff08;2&#xff09;编译成字节码文件 - HelloWorld.class&#xff08;3&…

网络原理TCP_IP

文章目录 应用层自定义协议 传输层udp协议TCP协议1.确认应答2.超时重传3.连接管理建立连接, 三次握手断开连接, 四次挥手tcp的状态 4.滑动窗口5.流量控制6.拥塞控制7.延时应答8.携带应答9.面向字节流10.异常情况 网络层IP协议地址管理路由选择 数据链路层以太网 应用层 自定义…

【Greenhills】MULTIIDE集成第三方的编辑器进行源文件编辑工作

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 在使用GHS进行工作的时候&#xff0c;可以集成第三方的编辑器进行源文件编辑工作 2、 问题场景 用于解决在GHS中进行项目开发时&#xff0c;对于GHS的编辑器使用不习惯&#xff0c;想要切换到其他第三方的编辑…

差分与前缀和模板题(蓝桥杯 C++ 题目 注解)

目录 题目一&#xff08;大学树木要打药 差分&#xff09;&#xff1a; 代码&#xff1a; 题目二&#xff08;小明的彩灯 差分&#xff09;&#xff1a; 代码&#xff1a; 题目三&#xff08;区间更新 差分&#xff09;&#xff1a; 代码&#xff1a; 题目四&#xff08;…

python基于django的药品进销存管理系统elsb2

本系统是通过面向对象的python语言搭建系统框架&#xff0c;通过关系型数据库MySQL存储数据。使用django框架进行药店药品的信息管理&#xff0c;用户只需要通过浏览器访问系统即可获取药店药品信息&#xff0c;并可以在线管理&#xff0c;实现了信息的科学管理与查询统计。本文…

了解一下c++的小语法——步入c++

前言&#xff1a;c是一门既面向对象又面向过程的语言。 不同于java纯粹的面向对象和c纯粹的面向过程。 造成c该特性的原因是c是由本贾尼大佬在c的基础上增添语法创建出来的一门新的语言。 它既兼容了c&#xff0c; 身具面向过程的特性。 又有本身的面向对象的特性。 面向对象和…

selenium-java 通过配置xml文件并发运行类或者方法

1、打开idea允许某个class类&#xff0c;可以在控制台看到运行路径的下的配置文件如下图&#xff1a; 2、将路径复制到本地路径中找到temp-testng-customsuite.xml文件 3、复制该文件到项目的根目录下&#xff0c;可以修改文件名称&#xff0c;如下图 4、如图所示&#xff0c;通…

【Python】成功解决TypeError: ‘int‘ object is not iterable

【Python】成功解决TypeError: ‘int’ object is not iterable &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到…

【Flink网络数据传输(4)】RecordWriter(下)封装数据并发送到网络的过程

文章目录 一. RecordWriter封装数据并发送到网络1. 数据发送到网络的具体流程2. 源码层面2.1. Serializer的实现逻辑a. SpanningRecordSerializer的实现b. SpanningRecordSerializer中如何对数据元素进行序列化 2.2. 将ByteBuffer中间数据写入BufferBuilder 二. BufferBuilder申…

java ~ word模板填充字符后输出到指定目录

word文件格式&#xff1a; jar包&#xff1a; <dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.10.0</version></dependency>样例代码&#xff1a; // 封装参数集合Map<String, Ob…

【异常处理】BadSqlGrammarException低级SQL语法异常

报错 org.springframework.jdbc.BadSqlGrammarException: ### Error querying database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use …

MYSQL5.7报1205 - Lock wait timeout exceeded; try restarting transaction

简介 今天使用navicate操作添加时&#xff0c;mysql报错误&#xff0c;错误如下 原因 这个问题的原因是在mysql中产生了事务A&#xff0c;执行了修改的语句&#xff0c;比如&#xff1a; update t1 set aget18 where id1;此时事务并未进行提交&#xff0c;事务B开始运行&am…

Linux_防火墙无法启动问题

当查看防火墙状体的时候报如下错误 ● firewalld.service - firewalld - dynamic firewall daemonLoaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)Active: inactive (dead) since 四 2024-03-07 07:42:16 CST; 7s agoDocs: man…

【黑马程序员】STL实战--演讲比赛管理系统

演讲比赛管理系统 需求说明 比赛规则 程序功能 创建管理类 功能描述 提供菜单界面与用户交互 对演讲比赛流程进行控制 与文件的读写交互 创建演讲比赛管理类 新建speechManager.hpp #pragma once#include <iostream>using namespace std;// 设计演讲比赛类 clas…

光线追踪5- Surface normals and multiple objects

首先&#xff0c;让我们获取一个表面法线&#xff0c;以便进行着色。这是一个垂直于交点处表面的向量。在我们的代码中&#xff0c;我们需要做一个重要的设计决定&#xff1a;法线向量是否为任意长度&#xff0c;还是将其归一化为单位长度。 诱人的是&#xff0c;如果不…

react高阶组件:如何同时兼容class类组件和函数式组件。

场景&#xff1a; 每个页面都要实现分享功能&#xff0c;但是页面有些是用class类&#xff0c;有些又直接是函数式。 方案1&#xff1a; 写2套方法。各自引用。&#xff08;维护不太好&#xff0c;改要改2遍&#xff09; 方案2&#xff1a; 可以封一个 jsx的组件&#xff0c…

服务器cpu占用高没看到进程

现象&#xff1a; 1. 今天连服务器发现root密码被改了&#xff0c;再改回去&#xff0c;登录发现服务器很卡&#xff0c;top查看&#xff0c;可用的cpu为0&#xff0c;但是没看到明显的进程&#xff0c;很显然中了病毒 2. 发现crontab -l有异常的定时计划&#xff0c;给删除掉 …

DailyNotes个人笔记管理工具

DailyNotes 是记录笔记和跟踪任务的应用程序&#xff0c;使用markdown进行编辑 部署 下载镜像 docker pull m0ngr31/dailynotes创建目录并授权 mkdir -p /data/dailynotes/config_dir chmod -R 777 /data/dailynotes启动容器 docker run -d --restart always --name mynot…