【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗)

这篇具有很好参考价值的文章主要介绍了【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先

快乐的流畅:个人主页
个人专栏:《C语言》《数据结构世界》《进击的C++》
远方有一堆篝火,在为久候之人燃烧!

引言

之前学习的AVL树,是一种平衡二叉搜索树,它追求绝对平衡,从而导致插入和删除性能较差。而今天学习的红黑树,是另一种平衡二叉搜索树,它追求相对平衡,使得增删查改的性能都极佳,时间复杂度皆为O(log2N),是数据结构中的精华,天才般的设想!

一、红黑树的概念

红黑树,顾名思义,其节点有红和黑两种颜色。

之所以新增结点颜色的标记,是因为通过结点着色方式的限制,能够让红黑树的最长路径不超过最短路径的两倍,以保证相对平衡。

【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先


红黑树满足五条性质:

  1. 所有结点非黑即红
  2. 根结点为黑色
  3. NIL结点为黑色
  4. 红色结点的子结点必为黑色
  5. 任意结点到其叶子NIL结点的所有路径,都包含相同的黑色结点

在红黑树中,NIL节点(也称为空节点)是叶子节点的一种特殊表示。它们不是实际存储数据的节点,而是树结构中的占位符,用于定义树的边界。所有的红黑树都以NIL节点为叶子节点,这些NIL节点在视觉上通常不被画出来。


性质解读:

  • 性质4:表明不能有连续的红色结点
  • 性质4+性质5:
    • 理论最短路径:全为黑色结点
    • 理论最长路径:红黑相间
      【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先

这样,就保证了最长路径不超过最短路径的两倍。

二、红黑树的模拟实现

2.1 结点

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

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

细节:

  1. 使用三叉链,增加了指向parent的指针
  2. 使用KV模型,数据存储键值对pair
  3. 结点储存颜色,同时颜色使用枚举
  4. 结点的颜色初始化为红色

说明:为什么结点的颜色初始化为红色呢?因为插入新节点时(不为根部),如果插入黑色,就会直接破坏性质5,导致每条路径黑结点数目不同;而如果插入红色,有可能不会破坏性质4,所以结点初始化为红色更优。

2.2 成员变量

template<class K, class V>
class RBTree
{
protected:
	typedef RBTreeNode<K, V> Node;
public:
protected:
	Node* _root = nullptr;
};

2.3 插入

因为红黑树也是二叉搜索树,所以默认成员函数和遍历与之前写的没什么不同,这里重点讲解红黑树的插入。

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);
	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->_right == parent)//uncle在左,parent在右
		{
			Node* uncle = grandparent->_left;
			if (uncle && uncle->_col == RED)//uncle为红,变色+向上调整
			{
				parent->_col = uncle->_col = BLACK;
				grandparent->_col = RED;

				cur = grandparent;
				parent = cur->_parent;
			}
			else//uncle为空或为黑,变色+旋转
			{
				if (parent->_right == cur)//左单旋
				{
					RotateL(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				else//右左旋
				{
					RotateR(parent);
					RotateL(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
			}
		}
		else//parent在左,uncle在右
		{
			Node* uncle = grandparent->_right;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandparent->_col = RED;

				cur = grandparent;
				parent = cur->_parent;
			}
			else
			{
				if (parent->_left == cur)//右单旋
				{
					RotateR(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				else//左右旋
				{
					RotateL(parent);
					RotateR(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
			}
		}
	}
	_root->_col = BLACK;

	return true;
}

思路:

  1. 以二叉搜索树的方式正常插入
  2. 讨论并调整结点的颜色,以及调整结构,使之满足红黑树的性质

循环条件:while (parent && parent->_col == RED)

保证了parent存在且为红,grandparent存在且为黑


情况一:uncle在左,parent在右

如果uncle存在且为红色

【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先

处理方法:

  1. 将parent和uncle变黑,grandparent变红
  2. cur = grandparent,parent = cur->_parent,继续向上调整
  3. 防止grandparent为根节点却变红,在循环结束后将根节点变为黑色
如果uncle不存在,或者存在且为黑色

当cur在右部外侧时:
【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先
处理方法:

  1. 先对grandparent进行左单旋
  2. 再将parent变黑,grandparent变红

当cur在右部内侧时:
【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先

处理方法:

  1. 先对parent进行右单旋
  2. 再对grandparent进行左单旋
  3. 最后将cur变黑,grandparent变红

情况二:parent在左,uncle在右

如果uncle存在且为红色

【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先

处理方法:

  1. 将parent和uncle变黑,grandparent变红
  2. cur = grandparent,parent = cur->_parent,继续向上调整
  3. 防止grandparent为根节点却变红,在循环结束后将根节点变为黑色
如果uncle不存在,或者存在且为黑色

当cur在左部外侧时:
【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先
处理方法:

  1. 先对grandparent进行右单旋
  2. 再将parent变黑,grandparent变红

当cur在左部内侧时:
【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先

处理方法:

  1. 先对parent进行左单旋
  2. 再对grandparent进行右单旋
  3. 最后将cur变黑,grandparent变红

红黑树插入的核心口诀uncle存在且为红,变色+向上调整,uncle不存在或为黑,变色+旋转


附上旋转的实现

void RotateL(Node* parent)
{
	Node* grandparent = 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 (grandparent)
	{
		if (grandparent->_right == parent)
		{
			grandparent->_right = subR;
		}
		else
		{
			grandparent->_left = subR;
		}
	}
	else
	{
		_root = subR;
	}
	subR->_parent = grandparent;
}

void RotateR(Node* parent)
{
	Node* grandparent = 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 (grandparent)
	{
		if (grandparent->_right == parent)
		{
			grandparent->_right = subL;
		}
		else
		{
			grandparent->_left = subL;
		}
	}
	else
	{
		_root = subL;
	}
	subL->_parent = grandparent;
}

三、红黑树的验证

bool IsBalance()
{
	if (_root && _root->_col == RED)
	{
		cout << "根结点为红色" << endl;
		return false;
	}

	int benchMark = 0;//基准值
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
		{
			++benchMark;
		}
		cur = cur->_right;
	}

	return Check(_root, 0, benchMark);
}

bool Check(Node* root, int blackNum, int benchMark)
{
	if (root == nullptr)
	{
		if (blackNum != benchMark)
		{
			cout << "某条路径黑色结点数量不相等" << endl;
			return false;
		}
		return true;
	}

	if (root->_col == BLACK)
	{
		++blackNum;
	}

	if (root->_col == RED && root->_parent && root->_parent->_col == RED)
	{
		cout << "存在连续的红色结点" << endl;
		return false;
	}

	return Check(root->_left, blackNum, benchMark)
		&& Check(root->_right, blackNum, benchMark);
}

细节:

  1. 验证根节点是否为黑
  2. 先计算出一条路径的黑色结点个数作为基准值,再在递归中比较每条路径的黑色结点是否相等
  3. 若该节点为红,检测其parent是否为红,判断是否存在连续的红色节点

四、红黑树的性能

4.1 优势

红黑树是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对AVL树而言,降低了插入和旋转的次数

4.2 适用场景

因此,在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。


【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗),进击的C++,数据结构世界,c++,开发语言,红黑树,数据结构,深度优先文章来源地址https://www.toymoban.com/news/detail-845466.html

真诚点赞,手有余香

到了这里,关于【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【C++练级之路】【Lv.14】二叉搜索树(进化的二叉树——BST)

    【C++练级之路】【Lv.14】二叉搜索树(进化的二叉树——BST)

    快乐的流畅:个人主页 个人专栏:《C语言》《数据结构世界》《进击的C++》 远方有一堆篝火,在为久候之人燃烧! 二叉树在之前的数据结构章节讲解过,当时使用C来实现。而如今学习的二叉搜索树,便是二叉树的进阶,也更适合使用C++来实现。 二叉搜索树(BST,Binary Se

    2024年03月23日
    浏览(14)
  • 【C++练级之路】【Lv.17】【STL】set类和map类的模拟实现

    【C++练级之路】【Lv.17】【STL】set类和map类的模拟实现

    快乐的流畅:个人主页 个人专栏:《C语言》《数据结构世界》《进击的C++》 远方有一堆篝火,在为久候之人燃烧! STL库中的set类和map类,其底层原理都是 通过红黑树来实现 的。尽管set和map可以各自实现一棵红黑树,但是为了提高代码的复用率,STL库中将红黑树进行了一定

    2024年04月16日
    浏览(17)
  • 【C++练级之路】【Lv.20】位图和布隆过滤器(揭开大数据背后的神秘面纱)

    【C++练级之路】【Lv.20】位图和布隆过滤器(揭开大数据背后的神秘面纱)

    快乐的流畅:个人主页 个人专栏:《算法神殿》《数据结构世界》《进击的C++》 远方有一堆篝火,在为久候之人燃烧! 哈希映射 的思想,在实际中有许多运用,之前介绍的 哈希表 是一种经典的应用场景,而今天我们将了解其他的哈希数据结构—— 位图和布隆过滤器 ,它

    2024年04月12日
    浏览(15)
  • 【C++练级之路】【Lv.4】类和对象(下)(初始化列表,友元,static成员,编译器的优化)

    【C++练级之路】【Lv.4】类和对象(下)(初始化列表,友元,static成员,编译器的优化)

    欢迎各位小伙伴关注我的专栏,和我一起系统学习C++,共同探讨和进步哦! 学习专栏 : 《进击的C++》 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象

    2024年02月04日
    浏览(19)
  • 【C++练级之路】【Lv.3】类和对象(中)(没掌握类的6个默认成员函数,那你根本就没学过C++!)

    【C++练级之路】【Lv.3】类和对象(中)(没掌握类的6个默认成员函数,那你根本就没学过C++!)

    欢迎各位小伙伴关注我的专栏,和我一起系统学习C++,共同探讨和进步哦! 学习专栏 : 《进击的C++》 在C++的学习中,类和对象章节的学习尤为重要,犹如坚固的地基,基础不牢,地动山摇;而默认成员函数的学习,在类和对象的学习里最为重要。所以要 学好C++,学好默认

    2024年02月04日
    浏览(16)
  • 【C++练级之路】【Lv.2】类和对象(上)(类的定义,访问限定符,类的作用域,类的实例化,类的对象大小,this指针)

    【C++练级之路】【Lv.2】类和对象(上)(类的定义,访问限定符,类的作用域,类的实例化,类的对象大小,this指针)

    欢迎各位小伙伴关注我的专栏,和我一起系统学习C++,共同探讨和进步哦! 学习专栏 : 《进击的C++》 C语言是 面向过程 的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C++是基于 面向对象 的,关注的是对象,将一件事情拆分成不同的对象,靠对象

    2024年02月03日
    浏览(15)
  • 【C++进阶06】红黑树图文详解及C++模拟实现红黑树

    【C++进阶06】红黑树图文详解及C++模拟实现红黑树

    1.1 红黑树的概念 AVL树用平衡因子让树达到高度平衡 红黑树可以认为是AVL树的改良 通过给每个节点标记颜色让树接近平衡 以减少树在插入节点的旋转 在每个结点新增一个存储位表示结点颜色 可以是Red或Black 通过对任何一条从根到叶子的路径上 各个结点着色方式的限制 红黑

    2024年01月21日
    浏览(12)
  • 【C++高阶(四)】红黑树深度剖析--手撕红黑树!

    【C++高阶(四)】红黑树深度剖析--手撕红黑树!

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C++从入门到精通⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习C++   🔝🔝 如果说发明AVL树的人是天才,那么 发明红黑树的人可以称为天才中的 精英!为什么AVL树这么强大但是没啥 人用呢?就是因为红黑树比你还好

    2024年02月05日
    浏览(8)
  • 【C++进阶5-红黑树】噩梦般的存在?手提AVLTree暴揍红黑树!

    【C++进阶5-红黑树】噩梦般的存在?手提AVLTree暴揍红黑树!

    今天,带来无数人的噩梦——红黑树的讲解。文中不足错漏之处望请斧正! 如果还没看过AVLTree讲解的一定要去看看,看完才能更好理解红黑树! 红黑树是自平衡的二叉搜索树。 红黑树的规则: 每个结点非黑即红 根结点为黑 叶子结点为黑(此处的叶子结点指空结点) 不能有

    2024年02月07日
    浏览(9)
  • C++【红黑树】

    C++【红黑树】

    ✨个人主页: 北 海 🎉所属专栏: C++修行之路 🎃操作环境: Visual Studio 2019 版本 16.11.17 红黑树是平衡二叉搜索树中的一种,红黑树性能优异,广泛用于实践中,比如 Linux 内核中的 CFS 调度器就用到了红黑树,由此可见红黑树的重要性。红黑树在实现时仅仅依靠 红 与 黑 两

    2024年02月10日
    浏览(9)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包