红黑树的插入

news2024/9/29 13:32:29

目录

一、红黑树

二、红黑树节点的定义

三、红黑树的插入

3.1按照二叉搜索的树规则插入新节点

3.2检测新节点插入后红黑树的情况

3.3红黑树插入代码总体实现 

四、红黑树的验证

五、红黑树和AVL树的比较


一、红黑树

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

  • 红黑树的性质:
  1.  每个节点不是红色就是黑色
  2. 根节点和叶子节点(指空叶子节点)都是黑色
  3. 红节点的左右孩子节点一定为黑色,也就是说不存在连续的红节点
  4. 从根到每一个叶子结点的每一条路径上黑节点的数量是相同的
  5. 以上的4个性质保证了红黑树的最长路径不超过最短路径的两倍

二、红黑树节点的定义

enum Colour
{
	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;
	Colour _col;


	RBTreeNode(const pair<K,V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_col(RED)
	{}

};

 红黑树的节点要默认给成红色,因为我们在插入一个新节点时该新节点必须为红节点。如果插入的是黑节点,那么该黑节点所在的路径上黑色节点的数量就会改变,但是红黑树必须保证每一跳路径上黑色节点的数量相同,插入黑节点会影响红黑树的所以路径。

三、红黑树的插入

  • 红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

3.1按照二叉搜索的树规则插入新节点

    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);
		cur->_col = RED;
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

    ......

    }

 3.2检测新节点插入后红黑树的情况

  • 因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何 性质,则不需要调整;
  • 但当新插入节点的双亲节点颜色为红色时,就违反了性质3:不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
  • 约定:cur为当前节点,parent为父节点,grandparent为祖父节点,uncle为叔叔节点

1.当uncle存在且为红节点 

  • 调整规则:parent和uncle变为黑节点,grandparent变为红节点,cur更新到grandparent的位置;如果cur的parent为红,继续向上调整更新;如果cur的parent为黑节点,调整结束,如果cur为根结点,cur变黑,结束调整
  • 注意:图中的树可能是一棵完整的树,也可能是树的一部分子树

 2.uncle不存在

  • cur一定为新插入的节点,如果cur不是新插入的节点,那么cur和parent就一定会有一个黑色节点,那就不满足每条路径上黑色节点数量相同的规则了
  • uncleb不存在的时候,假定parent为grandparent的左孩子,调整步骤分为旋转+变色,旋转+变色也存在两种不同情况:
  1. 如果插入位置在parent的左边,grandparent、parent、cur都在一条直线上,此时只需要进行右单旋,再将parent变黑,grandparent变红即可
  2. 如果插入位置在parent的右边,grandparent、paren、cur是一条折线,此时需要先以parent为旋转点进行左单旋,再以cur为旋转点进行右单旋,旋转后将cur变黑,grandparent变红即可
  • uncleb不存在的时候,假定parent为grandparent的右孩子,分析过程同parent为左孩子的过程相同,只是旋转方向发生了变化

3uncle存在且为黑 

  • cur的原始颜色为黑节点,经过向上调整变色后变为红节点
  • uncleb存在且为黑的时候,假定parent为grandparent的左孩子,uncle为右孩子,调整步骤分为旋转+变色,旋转+变色也存在两种不同情况:
  1. 如果cur在parent的左边,grandparent、parent、cur都在一条直线上,此时只需要进行右单旋,再将parent变黑,grandparent变红即可
  2. 如果cur在parent的右边,grandparent、paren、cur是一条折线,此时需要先以parent为旋转点进行左单旋,再以cur为旋转点进行右单旋,旋转后将cur变黑,grandparent变红即可
  • uncleb存在且为黑的时候,假定parent为grandparent的右孩子,uncle为左孩子,分析过程同parent为左孩子uncle为右孩子的过程相同,只是旋转方向发生了变化

3.3红黑树插入代码总体实现 

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);
		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* grandparent = parent->_parent;
			if (grandparent->_left == parent)
			{
				Node* uncle = grandparent->_right;
				//uncle存在且为红
				if (uncle&& uncle->_col == RED)
				{
					//变色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandparent->_col = RED;

					//继续向上调整
					cur = grandparent;
					parent = cur->_parent;
				}
				//uncle不存在 或者 存在且为黑
				else
				{
					//右单旋
					if (parent->_left == cur)
					{
						RotateR(grandparent);
						parent->_col = BLACK;
						grandparent->_col = RED;
					}
					//左右双旋
					else
					{
						RotateL(parent);
						RotateR(grandparent);

						cur->_col = BLACK;
						grandparent->_col = RED;
					}
					break;
				}
			}
			else
			{
				Node* uncle = grandparent->_left;
				//uncle存在且为红
				if (uncle&& uncle->_col == RED)
				{
					//变色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandparent->_col = RED;

					//继续向上调整
					cur = grandparent;
					parent = cur->_parent;
				}
				//uncle不存在 或者 存在且为黑
				else
				{
					//左单旋
					if (parent->_right == cur)
					{
						RotateL(grandparent);
						parent->_col = BLACK;
						grandparent->_col = RED;

					}
					//右左双旋
					else
					{
						RotateR(parent);
						RotateL(grandparent);
						cur->_col = BLACK;
						grandparent->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
	}

四、红黑树的验证

  • 红黑树的检测分为两步:
  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列) 
  2. 检测其是否满足红黑树的性质 
  • ①如果有连续的红节点,一定不满足红黑树的性质,返回false
  • ②验证每一条路径上黑色节点的数量是否都相同
  • ③先计算其中一条路径上黑色节点的数量作为基准值(代码中计算的是最左侧路径上黑色节点的数量),再计算其他路径上黑色节点的数量,判断是否与基准值一样,一样返回true,不一样返回false
bool IsBalance()
	{
		return IsBalance(_root);
	}

	bool IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;

		//连续红节点
		if (root->_col == RED && root->_parent && root->_parent->_col == RED)
		{
			cout << root->_kv.first << ":有连续红节点" << endl;
			return false;
		}

		//基准值
		Node* cur = root;
		int bechmark = 0;
		while (cur)
		{
			if (cur->_col == BLACK)
			    bechmark++;

			cur = cur->_left;
		}

		return checkcolour(root, 0, bechmark);
	}

	bool checkcolour(Node* root, int blacknum, int bechmark)
	{
		if (root == nullptr)
		{
			if (blacknum != bechmark)
			{
				return false;
			}

			return true;
		}

		if (root->_col == BLACK)
			blacknum++;

		return checkcolour(root->_left, blacknum, bechmark) && checkcolour(root->_right, blacknum, bechmark);
	}

 五、红黑树和AVL树的比较

  • 红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(logN)
  • 红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数
  • AVL树虽然在查找的高度上会比红黑树更优一点点,但是AVL树的增删需要不断地改变树的结构
  • 红黑树在经常进行增删的结构中,性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。 
  • 红黑树的应用
  1. C++ STL库 -- map/set、mutil_map/mutil_set
  2. Java 库
  3. linux内核
  4.  其他一些库

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

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

相关文章

如何申请开通商家转账到零钱【成功经验分享】

商家转账到零钱功能是微信支付为商户提供的一项便捷服务&#xff0c;允许商户直接将资金转入用户的微信钱包。鉴于很多商户在申请商家转账到零钱时被过时的、错误的经验文章所误导&#xff0c;以我们上万次成功开通商家转账到零钱功能的经验整理这篇文章&#xff0c;希望能对新…

整套厨帽检测算法样本、模型、源码和厨帽算法识别应用方案介绍

厨帽检测算法的应用方案主要涉及技术选型、硬件配置、软件集成、部署与监控以及应对实际挑战等多个方面。以下是一个详细的应用方案概述&#xff1a; 一、技术选型 深度学习技术&#xff1a;厨帽检测算法主要基于深度学习技术&#xff0c;特别是卷积神经网络&#xff08;CNN&…

OpenCV几何图像变换(10)透视变换函数warpPerspective()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 warpPerspective 函数使用指定的矩阵对源图像进行透视变换&#xff1a; dst ( x , y ) src ( M 11 x M 12 y M 13 M 31 x M 32 y M 33 , M…

信息流产品场景及数据指标体系

一、信息流产品的场景 我们分别从用户、内容生产者和平台的角度&#xff0c;描述他们在信息流产品里的消费场景&#xff0c;便于理解阐述的指标体系。 1、用户在信息流产品里的消费的场景 小明同学&#xff0c;每天打开 5 次 App&#xff0c;刷新内容 20 次&#xff0c;浏览了…

高性能、可扩展、支持二次开发的java版本企业电子招标采购系统源码

在数字化时代&#xff0c;企业需要借助先进的数字化技术来提高工程管理效率和质量。招投标管理系统作为企业内部业务项目管理的重要应用平台&#xff0c;涵盖了门户管理、立项管理、采购项目管理、采购公告管理、考核管理、报表管理、评审管理、企业管理、采购管理和系统管理等…

苹果手机如何恢复微信好友?更新6个方法,快来收藏!

场景一&#xff1a;想查看某个好友的朋友圈&#xff0c;却不小心点击了删除好友。 场景二&#xff1a;与好友吵架了&#xff0c;一气之下删了好友&#xff0c;却不知如何重新恢复好友。 …… …… 除了上述的两种场景之外&#xff0c;我们可能还会因为其他的原因与好友失去微…

LLM - 从头开始实现 LLaMA3 的网络结构与推理流程 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/141462669 LLaMA3 是 Meta 的最新大语言模型&#xff0c;在整体网络设计进行多项升级&#xff0c;显著提升了模型的性能和效率&#xff0c;重要的…

AMEYA360:上海雷卯MOSFET器件参数:TJ、TA、TC到底讲啥?

近日&#xff0c;经常被问及MOSFET器件的参数计算问题。在本文中&#xff0c;AMEYA360将分享关于MOSFET中几个关键温度参数的计算方法&#xff1a;TJ(结温)、TA(环境温度)和TC(外壳温度)。 1. MOSFET温度参数的重要性 在电力电子应用中&#xff0c;温度是影响MOSFET性能和寿命的…

探索大型多模态智能代理的前沿进展

人工智能咨询培训老师叶梓 转载标明出处 在人工智能领域&#xff0c;代理被定义为能够感知环境并基于这些感知做出决策以实现特定目标的系统。尽管早期的代理在特定领域表现出了专业性&#xff0c;但它们通常缺乏适应性和泛化能力&#xff0c;现实世界的场景往往涉及超出文本的…

WinTune 系统基准测试:让你的电脑性能飞速提升

前言 你是否曾经为了等待电脑开机而焦急万分&#xff1f;是否因为系统卡顿而错过了重要的工作截止日期&#xff1f;是否渴望在繁忙的工作中找到一丝轻松&#xff0c;让加班成为过去式&#xff1f;如果你有这些烦恼&#xff0c;那么可以试试 WinTune 这款工具&#xff1b;它是一…

2024年电工(高级)证考试题库及电工(高级)试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年电工&#xff08;高级&#xff09;证考试题库及电工&#xff08;高级&#xff09;试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#…

Python接口自动化测试详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、环境搭建 python unittest requests实现http请求的接口自动化Python的优势&#xff1a;语法简洁优美, 功能强大, 标准库跟第三方库灰常强大&#xff0c;建…

如何加密文档?电脑文件安全加密详细操作步骤(10种方法)

防患于未然&#xff0c;智者之举也。 文档与电脑文件的安全加密&#xff0c;正如古时城门深锁、密函暗藏&#xff0c;实为守护信息安全的智慧之举。 本文将引领您穿越古今&#xff0c;以十种详尽的方法&#xff0c;探讨如何在电脑上安全加密文档&#xff0c;确保您的信息固若金…

阿里云服务器的基本使用

1、购买云服务器 1. 注册阿里云账号&#xff0c;登录进去选择产品&#xff0c;阿里云目前有云服务器试用的政策&#xff0c;对于新手学习者&#xff0c;我们可以选择一个试用服务器 2. 选择服务器之后创建实例&#xff08;选择试用之后根据提示一步一步创建实例&#xff09;&…

关于shell输出颜色的事情

实例 # echo -e "\e[1;33;41m test content \e[0m"分析&#xff1a; 1、-e&#xff1a;转义起始符&#xff0c;等同于\033&#xff0c;表示定义一个转义序列 2、[&#xff1a;表示开始定义颜色 3、1;33;41&#xff1a;其中1表示高亮&#xff0c;33表示字体颜色为黄色…

ubuntu设置jupyter远程连接

一、配置远程连接 我是在unbuntu虚拟环境中操作的&#xff0c;&#xff08;要安装使用虚拟环境请看&#xff1a;ubuntu安装虚拟环境-CSDN博客&#xff09; step1&#xff1a;生成配置文件 jupyter notebook --generate-config 这样在~/.jupyter文件夹下就有 jupyter_noteboo…

AI绘画SD必学技能—从零开始训练你的专属Lora 模型!StableDiffusion模型训练保姆级教程建议收藏!

大家好&#xff0c;我是画画的小强 接触AI绘画的小伙伴&#xff0c;一定听过Lora。 Lora模型全称是&#xff1a;Low-Rank Adaptation of Large Language Models&#xff0c;可以理解为Stable-Diffusion中的一个插件&#xff0c;在生成图片时&#xff0c;Lora模型会与大模型结…

要做实施先做人

文/杨长春 作者简介&#xff1a;某IT公司项目总监&#xff0c;资深IT博主&#xff0c;专注于IT项目知识分享&#xff0c;著有《实战需求分析》、《软件需求分析实战》、《数字化管理软件实施》。 圣人曰&#xff0c;要做实施先做人。 作为一个软件项目的实施者&#xff0c;项目…

如何探索Sui DeFi生态

无论你是想进行tokens兑换、探索NFT世界&#xff0c;还是只是想借出资产以赚取奖励&#xff0c;Sui的DeFi生态都有适合你的内容。由于Sui原生的特性&#xff0c;这些apps能够应对DeFi中的常见挑战&#xff0c;例如通过DeepBook解决流动性问题。 一个健康的DeFi生态由几个关键应…

RFID光触发标签的特性、应用与传统RFID标签的差别

在当今数字化、智能化的时代浪潮中&#xff0c;RFID技术作为一种非接触式自动识别技术&#xff0c;已经在众多领域得到了广泛应用。而RFID光触发标签作为这一技术的创新发展&#xff0c;正以其独特的优势引领着行业的变革。 一、RFID光触发标签的特性 &#xff08;一&#xf…