【C++】红黑树

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


正文开始前给大家推荐个网站,前些天发现了一个巨牛的 人工智能学习网站, 通俗易懂,风趣幽默,忍不住分享一下给大家。 点击跳转到网站。

红黑树的概念

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

【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树

红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

有了这些规则也就意味着,红黑树的最短路径是全部都是黑色节点,最长路径就是一黑一红交替出现,因为每条路径都拥有相同给的黑色节点数量,假设每条路径的黑色节点数量为N,那所有路径的节点数量就在N ~ 2*N之间,但是最短路径和最长路径不一定存在。

红黑树实现

红黑树节点的定义

因为每个节点都存在颜色,并且不为黑色就为红色,所以我们可以定义一个表示颜色的枚举常量。红黑树依旧是三叉链。

enum Colour
{
	RED,
	BLACK
};

template <class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	Colour _col;
	pair<K, V> _kv;

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

template <class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv);
	
private:
	Node* _root = nullptr;
};

红黑树的实现

插入

插入刚开始依旧是搜索二叉树的规则,如果插入的值比当前节点的大就往右边走,比当前节点的值小,就往左边走,一直走到空。

思考
如果走到空了以后,我们插入了这个节点,那么这个节点的颜色给什么好呢?

如果我们给黑色的话,我们会违反规则4,因为在插入前,我们这棵树就是红黑树,所以每条路径的黑色节点数量是相同的,如果我们插入了黑色节点,会导致所有路径的黑色节点的数量不一样,我们需要修改所有的路径代价有点大,如果我们插入红色节点的话,如果插入后父亲是黑色的,就可以插入结束了,如果是红色,那么违反的也是规则三,不会影响所有路径,只需要调整当前路径就可以了,所以我们对于刚插入的节点应该给红色,然后在根据父亲的颜色去判断是否需要调整。

当我们插入了一个节点后,我们初始值给红色,如果父亲的颜色为黑色的话,那么就不违反红黑树的规则,可以直接插入结束,但是如果父亲的节点为红色,那么我们需要调整,调整分为好几种情况,我们一起来看一下。

下图 g表示爷爷(grandfather) ,p表示父亲(parent), u表示叔叔(uncle), c表示当前节点。

  1. 情况一
    u存在并且为红色
    【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树

a,b,c,d,e均为红黑树。cur可能是新插入,也有可能是下面更新上来的。
此时c和p和g一定是确定的,因为p为红色,g只能为黑色,不然插入前就不是红黑树,应该去查以前的问题。

此时是我们需要将p和u变为黑色,g变为红色。

【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树

此时有个小细节,g有可能是一颗红黑树的子树,也有可能是根节点,所以如果是根节点的话我们需要把它变黑。但是会比较麻烦,所以我们在插入的最后,不管怎么样,都把根节点的颜色变黑,就可以解决问题。

【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树

如果他不是根节点,那么爷爷的父亲也有可能是红色的,所以我们需要接着往上调整,我们需要把g给c,然后接着更新父亲,接着调整即可。

  1. 情况二
    u不存在/u存在且为黑

【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树

此时cur就是新插入节点。

【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树
此时如果d和e是空或者为红色节点,那么a,b,c就是每条路径一个黑色节点的红黑树,不管怎么样,a,b,c都是比d,c多一个黑色节点的红黑树。并且cur原来的颜色一定是黑色的,它一定是下面的节点变色变上来的。因为如果他本来就是红色的话,这棵树就出现了连续的红色节点,违反了规则三。

此时光靠变色时无法解决问题问题的,如果我们和上面一样,把p变黑,把g变红,那么右边的路径就会少一个黑色节点,所以光凭变色时无法解决这里的问题的。我看看上图,就会想到右单旋,没错,这里就是需要对grandfather进行一个右单旋,然后把p变黑,把g变红就可以解决这里的问题。
【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树

但是如果cur原本在p的右边呢?
【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树
此时就需要进行一个左右双旋。
先进行一个左单旋
【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树

在进行一个右单旋
【C++】红黑树,C++,数据结构与算法,c++,开发语言,数据结构,决策树
在旋转完成以后,需要将cur变黑,把grandfather变红,即可。

这是叔叔在爷爷的右边的两种情况,还有叔叔在爷爷的左边,和在右边的处理情况一样,类似就好了。

通过上面两个我们可以看到红黑树在插入以后,一直在变色,或者旋转+变色,本质都是在保持在插入一个节点后保持原来每条路径黑色节点的数量不发生变化,所以有些情况单凭变色是无法完成的,所以有些情况需要旋转+变色。

代码:

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);
	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 (parent == grandfather->_left)
		{
			Node* uncle = grandfather->_right;

			if (uncle && uncle->_col == RED)
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;

				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_left)
				{
					//    g
					//  c   u
					//p
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//    g
					//  p   u
					//    c
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;

				}
				break;
			}
		}
		else
		{
			//parent == grandfather->_right

			Node* uncle = grandfather->_left;

			if (uncle && uncle->_col == RED)
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;

				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_right)
				{
					RotateL(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
				}
				else
				{
					RotateR(parent);
					RotateL(grandfather);

					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
	}

	_root->_col = BLACK;

	return true;
}

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

	Node* parentParent = parent->_parent;

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

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


}

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

	Node* parentParent = parent->_parent;

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

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

	if (_root != parent)
	{
		if (parentParent->_left == parent)
		{
			parentParent->_left = subL;

		}
		else
		{
			parentParent->_right = subL;

		}
		subL->_parent = parentParent;

	}
	else
	{
		_root = subL;
		_root->_parent = nullptr;
	}
}

验证红黑树

我们写了一颗红黑树,那怎么来验证我们写的树是否出现问题呢?
本质还是围绕红黑树的几点规则来进行判断。

如何判断是否有连续的红色节点呢?
如我们拿到一个节点后,判断它的孩子的话,它的孩子有可能为空,也有可能为红色or黑色,处理起来会比较麻烦,而且它还有可能有一个孩子或者两个甚至没有,比较难处理,但是每一个节点的一定只有一个父亲,除了根节点,此时,我们判断如果当前节点是否为红色节点,如果是,就判断它的父亲节点,如果它的父亲节点也为红色,那么就可以认为出现了连续的红色节点,即红黑树出现异常。

如何验证每条路径是否有相同的黑色节点?

因为是递归的过程,记录起来会比较麻烦,但是我们可以用二叉树祖先那个题的第二个思路,使用一个栈把路径给记录下来,但是这里不需要用栈,我们只需要一个变量就可以完成,每条路径的最后一定是空节点,所以我们可以在遍历的同时如果遇到黑色节点接++i,这里要用传值,不能传引用,不然记录的就是整棵树的黑色节点的数量,然后当遇到空节点后把当前路径的黑色节点数量打印一下就可以了。

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

	if (_root->_col == RED)
	{
		return false;
	}

	int i = 0;
	return check(_root,i);
}
bool check(Node* root, int i)
{
	if (root == nullptr)
	{
		//cout << "->" << i << endl;
		return true;
	}

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

	if (root->_col == RED && root->_parent->_col == RED)
	{
		cout << "有连续的红色节点" << endl;
		return false;
	}
	return check(root->_left,i)
		&& check(root->_right,i);
}
	

红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。map和set的底层都是红黑树实现的。文章来源地址https://www.toymoban.com/news/detail-792243.html

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

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

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

相关文章

  • 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)
  • 数据结构入门-11-红黑树

    史上最负盛名的平衡二叉树–红黑树,但其实就是2-3树的一种实现 也是BST,每一个节点都有颜色 性质 看 后面推导出来的结论 2-3树 :和红黑树是等价的 满足BST的基本性质,但不是一种二叉树 有两种节点: 2-3 绝对平衡:根节点到叶子节点 一定相同 2.3.1 如何维护绝对平衡

    2023年04月17日
    浏览(41)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包