红 黑 树

2023-09-22 15:00:00

一、红黑树的概念

AVL 树中删除一个结点,旋转可能要持续到根结点,此时效率较低

红黑树也是一种二叉搜索树,通过在每个结点中增加一个位置来存储红色或黑色,并对结点的着色进行限制,使得该二叉搜索树的最长路径不超过最短路径的两倍,即红黑树是一颗近似平衡的二叉搜索树,他不像 AVL 树的平衡那么严格,所以红黑树在插入和删除时,也不需要大量的旋转,并且搜索效率差不了 AVL 多少

红黑树是一颗二叉搜索树并且满足如下规则:

  • 每个节点不是红色就是黑色
  • 根结点是黑色的
  • 每个红结点的左右孩子一定是黑色
  • 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点
  • 叶结点都是黑色的(这里的叶结点指的是空节点)

根据上述规则可以得到:最短路径:全黑结点的路径,最长路径:一黑一红的路径,所以红黑树可以保证最长路径不超过最短路径的一半

在这里插入图片描述

二、红黑树的实现

1. 红黑树的存储结构

// 结点的颜色
enum Color { RED, BLACK };

// 红黑树的结点
template<class K, class V>
struct RBTreeNode
{
	std::pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Color _color;	// 结点的颜色

	RBTreeNode<K, V>(const std::pair<K, V>& kv = std::pair<K, V>(K(), V()))
		: _kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _color(RED)	// 为了方便树的结构调整,新结点默认为红色
	{}
};

// 红黑树
template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	RBTree<K, V>()
		: _root(nullptr)
	{}

private:
	Node* _root;
};

2. 红黑树的插入

首先按照二叉搜索树的方式插入结点,保证插入结点之后还是二叉搜索树,为了方便树的结构调整,插入结点默认为为红色,当插入结点完成之后,可能会违反红黑树的性质,此时有三种情况

  • 插入结点的父节点是黑色:没有违反红黑树的性质

  • 插入结点的父节点是红色,叔节点存在且为红:违反了红黑树的性质,此时需要对父节点和爷爷结点进行变色

由于父节点是红色的,所以爷爷结点一定存在且为黑,变色完之后,如果 g 结点是根结点,则将 g 结点变为黑色,否则将 g 结点所在的子树当做新插入的结点,继续向上调整

在这里插入图片描述

  • 插入结点的父节点是红色,叔节点不存在或存在且为黑:违反了红黑树的性质,此时需要对爷爷结点所在的子树进行旋转然后再对结点进行变色

由于父节点是红色的,所以爷爷结点一定存在且为黑,变色完之后,子树的根结点是黑色的,不用继续向上调整

在这里插入图片描述

在这里插入图片描述

u 存在且为黑的情况,一定是由 u 存在且为红的情况继续向上调整而来的

// 右旋
void RotateR(Node* parent)
{
	Node* pparent = parent->_parent;
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	parent->_left = subLR;
	if (subLR) subLR->_parent = parent;

	subL->_right = parent;
	parent->_parent = subL;

	if (pparent == nullptr) _root = subL;
	else
	{
		if (pparent->_left == parent) pparent->_left = subL;
		else pparent->_right = subL;
	}
	subL->_parent = pparent;
}

// 左旋
void RotateL(Node* parent)
{
	Node* pparent = parent->_parent;
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	parent->_right = subRL;
	if (subRL) subRL->_parent = parent;

	subR->_left = parent;
	parent->_parent = subR;

	if (pparent == nullptr) _root = subR;
	else
	{
		if (pparent->_left == parent) pparent->_left = subR;
		else pparent->_right = subR;
	}
	subR->_parent = pparent;
}
		
// 插入
bool Insert(const std::pair<K, V>& kv)
{
	// 按照二叉搜索树的方式插入结点,保证该树插入结点之后还是二叉搜索树
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_color = 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 return false;
	}

	cur = new Node(kv);
	if (parent->_kv.first > kv.first) parent->_left = cur;
	else parent->_right = cur;

	cur->_parent = parent;

	// 更新颜色
	while (parent && parent->_color == RED)
	{
		Node* grandfather = parent->_parent;
		if (grandfather->_left == parent)
		{
			Node* uncle = grandfather->_right;
			
			// u 存在且为红
			// u 不存在或存在且为黑
			//		p 为 g 的左,cur 为 p 的左 右单旋
			//		p 为 g 的左,cur 为 p 的右 先左旋再右旋
			if (uncle && uncle->_color == RED)
			{
				grandfather->_color = RED;
				parent->_color = BLACK;
				uncle->_color = BLACK;

				// 继续判断是否违反了红黑树的性质
				cur = grandfather;
				parent = grandfather->_parent;
			}
			else
			{
				if (parent->_left == cur)
				{
					RotateR(grandfather);
					grandfather->_color = RED;
					parent->_color = BLACK;
				}
				else
				{
					RotateL(parent);
					RotateR(grandfather);
					grandfather->_color = RED;
					cur->_color = BLACK;
				}
			}
		}
		else
		{
			Node* uncle = grandfather->_left;

			// u 存在且为红
			// u 不存在或存在且为黑
			//		p 为 g 的右,cur 为 p 的右 左单旋
			//		p 为 g 的右,cur 为 p 的左 先右旋再左旋
			if (uncle && uncle->_color == RED)
			{
				grandfather->_color = RED;
				parent->_color = BLACK;
				uncle->_color = BLACK;

				// 继续判断是否违反了红黑树的性质
				cur = grandfather;
				parent = grandfather->_parent;
			}
			else
			{
				if (parent->_right == cur)
				{
					RotateL(grandfather);
					grandfather->_color = RED;
					parent->_color = BLACK;
				}
				else
				{
					RotateR(parent);
					RotateL(grandfather);
					grandfather->_color = RED;
					cur->_color = BLACK;
				}
			}
		}
	}

	_root->_color = BLACK;
	return true;
}
更多推荐

恩智浦i.MX8MM核心板在智能售货机产品中的应用方案-迅为电子

迅为i.MX8MM核心板在自动售货机产品中可以实现多种应用,提高自动售货机的功能和性能。以下是i.MX8MM核心板在自动售货机产品中的应用方案:支付和交易处理:i.MX8MM核心板可以用作自动售货机的支付和交易处理器,支持各种支付方式,如信用卡、手机支付、硬币和纸币识别,以提供便捷的购物体验。库存管理:核心板可用于实时

文举论金:黄金原油全面走势分析策略指导。

市场没有绝对,涨跌没有定势,所以,对市场行情的涨跌平衡判断就是你的制胜法宝。欲望!有句意大利谚语:让金钱成为我们忠心耿耿的仆人,否则,它就会成为一个专横跋扈的主人。空头,多头都能赚钱,唯有贪心不能赚。是你掌控欲望还是欲望掌控你?古人云:不积硅步无以至千里,不积小流无以成江海。希望这句话成为我们之间的共勉。自知!人贵自知

R语言绘图-3-Circular-barplot图

0.参考:https://r-graph-gallery.com/web-circular-barplot-with-R-and-ggplot2.html1.说明:利用ggplot绘制环状的条形图(circularbarplot),并且每个条带按照数值大小进行排列。2绘图代码:注意:绘图代码中的字体为“TimesNew

什么是单页面应用(SPA)?它们的优点和缺点是什么?

聚沙成塔·每天进步一点点⭐专栏简介⭐什么是单页面应用(SPA)?⭐SPA的优缺点是什么?⭐SPA中如何处理搜索引擎优化(SEO)?⭐什么是前端路由(Front-endrouting)在SPA中的作用是什么?⭐SPA中如何处理浏览器历史记录和页面刷新?⭐SPA通常使用哪些前端框架或库来简化开发?⭐写在最后⭐专栏简介前端入

WebGL笔记: 2D和WebGL坐标系对比和不同的画图方式, 程序对象通信,顶点着色器,片元着色器

WebGL坐标系canvas2d画布和webgl画布使用的坐标系都是二维直角坐标系,但它们坐标原点、y轴的坐标方向,坐标基底都不一样canvas2d坐标系的原点在左上角,x轴朝右,y轴朝下1个单位的宽就是一个像素的宽,1个单位的高就是一个像素的高,都是按像素来走webgl坐标系的原点在画布中心,x轴朝右,y轴朝上1个单

在线客服系统品牌排行榜

客服系统是针对企业和组织的客户服务领域开发和提供的一种信息化系统。它可以帮助企业更好地管理与顾客之间的沟通、反馈和服务等。随着互联网技术和人工智能技术的不断发展,市场上的客服系统产品越来越多,如何选择一款适合自己的产品成为众多企业和组织面临的问题。今天,我们为大家提供客户服务系统排行榜热门品牌榜,为大家在做选择时提供可

LeetCode 1588. Sum of All Odd Length Subarrays

Givenanarrayofpositiveintegersarr,returnthesumofallpossibleodd-lengthsubarraysofarr.Asubarrayisacontiguoussubsequenceofthearray.Example1:Input:arr=[1,4,2,5,3]Ou

ACT汽车电子与软件技术周回顾 | 龙智技术专家分享汽车行业中版本控制与静态扫描的最佳实践

在2023ACT汽车电子与软件技术周期间,我们对话了龙智资深顾问、技术支持部门负责人李培,他聚焦结合行业趋势、自身经验与过往成功案例,分享了版本控制与静态代码扫描在汽车行业中的应用与实践。此外,还对比分析了包括Git、SVN等的多款工具,为大家提供帮助与参考。对话龙智技术负责人、“TechnicalHero”李培,探索

springboot和vue:五、RESTful服务+HTTP状态码+swagger配置

RESTfulRESTful的特点每一个URI代表一种资源客户端使用GET、POST、PUT、DELETE四种表示操作方式的动词对服务端资源进行操作:POST用于新建资源(也可以用于更新资源),PUT用于更新资源资源的表现形式是JSON或者HTML。客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请

安全厂商安恒信息加入龙蜥社区,完成 与 Anolis OS 兼容适配

近日,杭州安恒信息技术股份有限公司(以下简称“安恒信息”)签署了CLA(ContributorLicenseAgreement,贡献者许可协议),正式加入龙蜥社区(OpenAnolis),并成为龙蜥社区安全联盟(OASA)首批成员单位。安恒信息于2007年创立,秉承着企业使命“构建安全可信的数字世界”,将数字经济安全视

自己动手写数据库:关系代数和查询树执行效率的推导

上几节我们完成了sql解释器的实现。通过解析sql语句,我们能知道sql语句想做什么,接下来就需要执行sql语句的意图,也就是从给定表中抽取所所需要的数据。要执行sql语句,我们需要了解所谓的“关系代数”,所谓代数本质上就是定义操作符和操作对象,在关系代数里,操作符有三种,分别为select,project和produ

热文推荐