C++——【红黑树】原理详解:定义、性质、插入变色与旋转实现

🌈个人主页 :@ꪔ小林Y

✨个人专栏 :《C++小白闯关日记》,《C语言小白闯关日记》,《数据结构入门------从原理到实战》,《拾光Linux》,《网络安全》

🍀代码信条 :每一行代码都是成长的脚印👣,每一次调试成功都是对坚持的回应

目录

红黑树

1.红黑树定义及规则

  • 定义 :红黑树是一颗二叉搜索树 ,他的每个结点增加一个存储位来表示结点的颜色(红/黑)。通过对任何一条从根到叶子的路径上各个节点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2倍 ,因而接近平衡。
  • 规则 :
    (1)每个节点不是红色就是黑色
    (2)根节点是黑色(注意红黑树可以是空树)
    (3)如果一个节点是红色,则他的两个孩子节点必须是黑色,也就是说任意一条路径不会有连续的红色节点
    (4)对于任意一个节点,从该节点到其所有NULL节点的简单路径上,均包含相同数量的黑色节点
  • 红黑树增删查改时间复杂度O(logN)

2.红黑树的实现

1)红黑树的基本结构

cpp 复制代码
//枚举表示颜色
enum Colour
{
	RED,
	BLACK
};
//key/value实现
template<class K,class V>
struct RETreeNode
{
	pair<K, V> _kv;
	RETreeNode<K, V>* _left;
	RETreeNode<K, V>* _right;
	RETreeNode<K, V>* _parent;
	Colour _col;
	RETreeNode(const patr<K, v>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};
template<class K,class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
private:
	Node* _root = nulptr;
};

2)红黑树的插入

插入一个值按照二叉搜索树的规则进行插入,须符合以下规则:

  1. 空树插入,新增节点为黑色;非空树插入,新增节点必须为红色。否则破坏规则4。
  2. 非空树插入后,新增为红,如果父亲节点是黑色 ,没有违反任何规则,插入结束。
  3. 非空树插入后,新增为红,如果父亲节点是红色 ,违反规则3,进一步分析: (假设新增节点c(cur),父亲节点p,爷爷节点g,叔叔节点u)

3.1情况一:变色

叔叔存在且为红即 :c为红,p为红,u存在且为红 ,则将p和u变黑,g变红。把g当做新的c继续向上处理。若g就是根,则再变为黑


3.2情况二:单旋+变色

叔叔不存在或者存在且为黑 :c为红,p为红,g为黑,u不存在或者存在且为黑。u不存在,则c一定是新增节点,u存在且为黑,则c一定不是新增,c之前是黑色的,是在c的子树中插入,符合情况一,变色将c从黑色变成红色,更新上来的。这里单纯的变色无法解决问题,需要变色+旋转:

  • 如果p是g的左,c是p的左 ,那么先以g 为旋转点进行右单旋 ,再把p变黑,g变红即可 。p 变成课这颗树新的根,这样子树黑色结点的数量不变,没有连续的红色结点了,且不需要往上更新,因为c的父亲是黑色还是红色或者空都不违反规则。

  • 如果p是g的右,c是p的右 ,那么先以g 为旋转点进行左单旋 ,再把p变黑,g变红即可 。p变成课这颗树新的根。

    例:
    3.3情况三:双旋+变色

  • 如果p是g的左,c是p的右 ,那么先以p 为旋转点进行左单旋 ,再以g 为旋转点进行右单旋 ,再把c变黑,g变红即可 。c 变成课这颗树新的根。

  • 如果p是g的右,c是p的左 ,那么先以p 为旋转点进行右单旋 ,再以g 为旋转点进行左单旋 ,再把c变黑,g变红即可 。c变成课这颗树新的根。

    例:

红黑树插入代码实现
cpp 复制代码
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;
		if (grandfather->_left = parent)
		{
			//    g
			//  p   u
			//c
			Node* uncle = grandfather->_right;
			//情况一:叔叔存在且为红,变色即可
			if (uncle && uncle->_col == RED)
			{
				//变色+继续往上处理
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;
				cur = grandfather;
				parent = cur->_parent;
			}
			else// 叔叔不存在或者叔叔存在且为黑
			{
				//    g
				//  p   u
				//c
				// 情况二:单旋+变色
				if (cur == parent->_left)
				{
					RotateR(geandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//     g
					//  p     u
					//	  c
					//情况三:双旋+变色
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
		else
		{
			//    g
			//  u   p
			Node* uncle = grandfather->_left;
			//情况一:叔叔存在且为红,变色即可
			if (uncle && uncle->_col == RED)
			{
				//变色+继续往上处理
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;
				cur = grandfather;
				parent = cur->_parent;
			}
			else// 叔叔不存在或者叔叔存在且为黑
			{
				//    g
				//  u   p
				//        c
				// 情况二:单旋+变色
				if (cur == parent->_right)
				{
					RotateL(geandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//     g
					//  u     p
					//	    c
					//情况三:双旋+变色
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
	}
	//不存在父亲
	_root->_col = BLACK;
	return true;
}

🎊到这儿,本期C++------【红黑树】定义及其实现的内容就结束了。如果文中有表述不准的地方,或是你有更清晰的理解思路,强烈欢迎在评论区留言交流------ 技术路上多碰撞,才能更快进步

觉得内容对你有帮助的话,别忘了点赞❤️➕收藏🌟,方便后续回顾复习;想跟着一起系统学习数据结构的朋友,也可以点击关注,下一期我们会聚焦更进一步的学习。不见不散✌️

相关推荐
leisoo80972 小时前
融资融券数据怎么查两融指标含义与杠杆观察方法 IG50免费开源股票数据API接口
开发语言·jvm·数据库·python·json
霸道流氓气质2 小时前
多Agent通信机制与协议设计完全指南:从FIPA-ACL到A2A/MCP的Java生产级实战
java·开发语言
无名猿2 小时前
constexpr 能力扩张:从 C++11 到 C++20 的编译期计算
c++·性能优化·现代c++·编译期
007张三丰2 小时前
C/C++ 内存管理详解:从内存分布到 new/delete 底层原理
java·c语言·c++·内存管理
weixin_419658312 小时前
CANoe 使用指南:从输出窗口到数据回放的完整实战教程
开发语言·功能测试·车载系统·自动化·汽车
当青春邂逅吉米多维奇2 小时前
C#图解教程(第5版) 同步方法
开发语言·c#
骇客野人2 小时前
Java BIO / NIO / AIO 完整详解 + 编程技巧
java·开发语言·nio
霸道流氓气质2 小时前
OpenTelemetry 入门与实战:Java Agent、Spring Boot Starter与LLM调用追踪示例
java·开发语言·spring boot