红黑树深入剖析【C++】

这篇具有很好参考价值的文章主要介绍了红黑树深入剖析【C++】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、红黑树概念

 二、红黑树节点结构设计

三、插入操作

 处理情况1

 处理情况2

处理情况3

 插入总结:

 四、插入操作源码

五、红黑树验证


一、红黑树概念

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

红黑树深入剖析【C++】,c++,c++,数据结构,算法

 红黑树还必须满足以下规则:

1. 每个结点不是红色就是黑色(非红即黑)
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(没有连续的红色)
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点(每条路径上黑色节点的数量相等)(从这一规则也可以得出,在插入一新节点时,该节点必须为红,才会满足该条件)
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点

 规则四演示:红黑树深入剖析【C++】,c++,c++,数据结构,算法

 二、红黑树节点结构设计

 因为红黑树节点非红即黑,所以可以用枚举的思想来例举红节点和黑节点。因为在插入的过程中,可能会存在翻转的情况,所以就需要一个节点的父节点_parent,左孩子_left,右孩子_right。

enum Colour
{
	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;  //值域
	Colour _col;  //颜色

	RBTreeNode(const pair<K, V>& kv)  //初始化
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
	{}
};

三、插入操作

根据规则4,可以得出在插入一个新节点的时候,这个节点一定是为红色的(上已证)。在插入红色节点的时候可能还是会造成红红节点的冲突(违反规则3),所以我们还需要进行变色+旋转的处理方法

红黑树深入剖析【C++】,c++,c++,数据结构,算法

 在插入新节点后,因为新节点的默认颜色是红色,因此:如果其父亲节点的颜色是黑色,没有违反红黑树任何规则,则不需要调整;但当新插入节点的父亲节点颜色为红色时,就违反了规则3不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:为方便我们进行变色和旋转的处理,我们定义父亲节点为p,叔叔节点为u,祖父节点为g,当前节点为cur(新增)。实际上,根据红黑树的规则,我们可以确定要发生变色或旋转操作时,cur、p、g节点一定是红、红、黑,如果不满足这种情况,那么肯定这颗红黑树在次之前就违背的红黑树的规则。所以变化操作主要取决于叔叔节点u的颜色。如下抽象图表示,s/b/c/de代表的是满足规则的子树。

红黑树深入剖析【C++】,c++,c++,数据结构,算法

 

 处理情况1

cur为红,p为红,g为黑,u存在且为红

 处理方法:叔叔u和父亲p变黑,祖父g变红,再往上进行处理,如果g是根节点,需要把g再变黑,因为根节点必须是黑(规则2)。再往上处理的过程中因为会存在当前g的父节点为红的情况,又再次冲突,所以要进行处理.红黑树深入剖析【C++】,c++,c++,数据结构,算法

红黑树深入剖析【C++】,c++,c++,数据结构,算法 

 

 处理情况2

cur为红且在外侧,p为红,g为黑,u不存在/u存在且为黑

u的两种情况:

1.如果u节点不存在,则cur一定是新插入节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路径黑色节点个数相同。

红黑树深入剖析【C++】,c++,c++,数据结构,算法

 

⒉.如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色的,现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改成红色。
 

红黑树深入剖析【C++】,c++,c++,数据结构,算法

 

 处理方法:单旋+变色。按p节点进行右旋,此时p为红,g为黑,再将p和g的颜色进行交换,即满足红黑树规则。(若p为g的右孩子,cur为p的右孩子,则进行左单旋转,p、g变色--p变黑,g变红)。处理完成后不需要再进行往上处理,因为此时p为黑,p的父亲节点为黑或红都不会对树产生影响。

红黑树深入剖析【C++】,c++,c++,数据结构,算法

处理情况3

cur为红且在内侧,p为红,g为黑,u不存在/u存在且为黑

 处理方法:双旋+变色

u不存在情况:

若p在g的左侧,cur在内侧,则先对g的左子树进行左旋,在对g这棵子树进行右旋;反之,p在g的右侧,cur在内存,就先右旋再左旋再交换cur和g的颜色。

红黑树深入剖析【C++】,c++,c++,数据结构,算法

 u存在情况:

像这种情况,都是由情况一经过处理后得来的,此时就需要先对g的左子树进行左单旋,旋转后,就会变为情况二,此时再根据情况二的解决方法进行解决,右旋+变色,旋转后将cur和g的颜色进行交换即可。(如果最开始p是在右子树,则操作相反,先右旋再左旋变色。)

红黑树深入剖析【C++】,c++,c++,数据结构,算法

 插入总结:

1.红黑树插入的节点一定为红色

2.处理三种可能的情况关键在于叔叔节点u

3.u存在且为红(情况一),将p和u节点变黑,g节点变红,若g为根节点就将g变为黑色

4.情况二和情况三都是由情况一经过变化后得来的

4.u不存在或存在且为黑,插入的节点cur在p的外侧(情况二),若p是左子树就进行右旋+交换p、g颜色;若p是右子树,反之,左旋+交换颜色。

5.u不存在或存在且为黑,插入节点cur在p的内侧(情况三),若p是左子树就先进行左旋变为情况二,再进行右旋+交换颜色。反之p为右子树,先进行右旋变为情况二,在进行左旋+交换颜色。

 四、插入操作源码

插入源码: 

	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)  // parent为红色就需要变色处理,因为插入的节点为红
		{
			Node* grandfater = parent->_parent;  //祖父节点
			assert(grandfater);
			assert(grandfater->_col == BLACK);
			// 关键看叔叔
			if (parent == grandfater->_left)
			{
				Node* uncle = grandfater->_right;  //叔叔节点
				// 情况一 : uncle存在且为红,变色+继续往上处理
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;
					// 继续往上处理
					cur = grandfater;  // 往上处理的时候把祖父当做插入的节点即cur
					parent = cur->_parent;
				}// 情况二+三:uncle不存在 + 存在且为黑
				else
				{
					// 情况二:右单旋+变色
					//     g 
					//   p   u
					// c
					if (cur == parent->_left)
					{
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						// 情况三:左右单旋+变色
						//      g 
						//   p     u
						//     c
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

					break;
				}
			}
			else // (parent == grandfater->_right)
			{
				Node* uncle = grandfater->_left;
				// 情况一
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;
					// 继续往上处理
					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					// 情况二:左单旋+变色
					//     g 
					//   u   p
					//         c
					if (cur == parent->_right)
					{
						RotateL(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						// 情况三:右左单旋+变色
						//     g 
						//   u   p
						//     c
						RotateR(parent);
						RotateL(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;
		return true;
	}

旋转操作的源码解析可以参考这篇博文:http://t.csdn.cn/iyMac

左旋:

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

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

		Node* ppNode = parent->_parent;

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

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

			subR->_parent = ppNode;
		}

	}

右旋:

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

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

		Node* ppNode = parent->_parent;

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

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

			subL->_parent = ppNode;
		}

	}

五、红黑树验证

验证方法:判断从根节点起的每一条子路径中的黑节点数是否相等(规则4)。利用递归的思想遍历每一条路径。再遍历第一条路径的时候,设置一个benchmark记录黑色节点数量,因为规则4,所以后面每一条路径的黑节点数应该都要与之相等,如果不等则不是红黑树。再遍历每一条路径的同时,也可以寻找是否存在连续的红节点(规则三),存在则不满足为红黑树。依次递归每一条路径。

源码:文章来源地址https://www.toymoban.com/news/detail-612896.html

	bool IsBalance()
	{
		if (_root == nullptr)
		{
			return true;
		}

		if (_root->_col == RED)
		{
			cout << "根节点不是黑色" << endl;
			return false;
		}

		// 黑色节点数量基准值
		int benchmark = 0;

		return PrevCheck(_root, 0, benchmark);
	}


	bool PrevCheck(Node* root, int blackNum, int& benchmark)
	{
		if (root == nullptr)
		{
			if (benchmark == 0)  
			// 遍历第一条路径的时候,记录他的黑节点数,后面每一条路径的黑节点数一个都和他相等
			{
				benchmark = blackNum;
				return true;
			}

			if (blackNum != benchmark)
			{
				cout << "某条黑色节点的数量不相等" << endl;
				return false;
			}
			else
			{
				return true;
			}
		}

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

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

		return PrevCheck(root->_left, blackNum, benchmark)
			&& PrevCheck(root->_right, blackNum, benchmark);
	}


void TestRBTree()
{
	size_t N = 1000;
	srand(time(0));
	RBTree<int, int> t1;
	for (size_t i = 0; i < N; ++i)
	{
		int x = rand();
		cout << "Insert:" << x << ":" << i << endl;
		t1.Insert(make_pair(x, i));
	}
	cout << "IsBalance:" << t1.IsBalance() << endl;  //打印1则是红黑树,否则不是
}

到了这里,关于红黑树深入剖析【C++】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 数据结构:红黑树讲解(C++)

    本文旨在 理解红黑树基本概念以及变色旋转规则 ,以理解C++ map 和 set 的底层原理,不会讲红黑树的删除操作。 对于基本的旋转操作(单旋和双旋),本文不会展开讲,详细讲解在这里: AVL树旋转讲解。 2.1概念 红黑树,是一种二叉搜索树,但在每个结点上 增加一个存储位表示

    2024年02月05日
    浏览(43)
  • Python - 深夜数据结构与算法之 AVL 树 & 红黑树

    目录 一.引言 二.高级树的简介 1.树 2.二叉树 3.二叉搜索树 4.平衡二叉树 三.AVL 树 ◆ 插入节点 ◆ 左旋 ◆ 右旋 ◆ 左右旋 ◆ 右左旋 ◆ 一般形式 ◆ 实际操作 ◆ 总结 四.红黑树 ◆ 概念 ◆ 示例 ◆ 对比 五.总结 前面我们介绍了二叉树、二叉搜索树、多叉树等基础的树形结构,

    2024年01月19日
    浏览(44)
  • 数据结构与算法(三):树论(树形结构、二叉树、二叉搜索树、红黑树、Btree&B+Tree、赫夫曼树、堆树)

    树论(树形结构、二叉树、二叉搜索树、红黑树、Btree、B+Tree、赫夫曼树、堆树) 在树形结构里面重要的术语: 结点:树里面的元素。 父子关系:结点之间相连的边 子树:当结点大于1时,其余的结点分为的互不相交的集合称为子树 度:一个结点拥有的子树数量称为结点的

    2024年02月01日
    浏览(94)
  • 【高阶数据结构】红黑树 {概念及性质;红黑树的结构;红黑树的实现;红黑树插入操作详细解释;红黑树的验证}

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

    2024年02月09日
    浏览(49)
  • 数据结构 | 红黑树

    节点的左边比节点的值小,右边比节点的值大。 节点要么是 红色 ,要么是 黑色 根节点 是黑色 叶子节点都是黑色的空节点 红黑树中红色节点的子节点都是黑色 从任一节点到叶子节点的所有路径都包含相同数目的黑色节点 在添加或者删除节点的时候,如果不满足这些性质会

    2024年01月21日
    浏览(44)
  • [数据结构]-红黑树

    前言 作者 : 小蜗牛向前冲 名言: 我可以接受失败,但我不能接受放弃   如果觉的博主的文章还不错的话,还请 点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、红黑树的基本知识  1、红黑树的概念 2、性质  二、红黑树的模拟实

    2024年02月04日
    浏览(46)
  • 【数据结构】红黑树

    🐱作者:一只大喵咪1201 🐱专栏:《数据结构与算法》 🔥格言: 你只管努力,剩下的交给时间! 在学习AVL树的时候,我们知道,当修改AVL树的结构(插入,删除)时,会通过旋转来保证平衡因子不超过1,所以频繁的修改结构会导致效率低下,今天我们学习的红黑树就完美解

    2023年04月17日
    浏览(49)
  • 数据结构——红黑树

    目录 概念 性质 结点的定义  插入 调整 当p是g的左孩子时 当p为g的右孩子时 插入完整代码 红黑树的检测 红黑树完整代码(包括测试数据)   红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是RED或BLACK。 通过对任何一条从根到叶子的路径

    2023年04月09日
    浏览(46)
  • 【数据结构-树】红黑树

    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学习,不断总结,共同进步,活到老学到老 导航 檀越剑指大厂系列:全面总

    2024年02月07日
    浏览(44)
  • 红黑树数据结构

    现在JAVASE中HashMap中底层源码是由数组+链表+红黑树进行设计的,然后很多地方也是用到红黑树,这里单独对红黑树数据结构进行简单的介绍。 目录 红黑树概念 红黑树的性质 自平衡规则 代码   红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可

    2024年02月01日
    浏览(40)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包