手撕红黑树、三种情况就可玩转红黑

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

红黑树认为,AVL数控制的太严格,越严格就会有越多的旋转
AVL树严格平衡,红黑树近似平衡

这样的话红黑树也会造成一些不好的后果,比如查找某些数据次数多一些,略慢一些,但是大部分情况还是在中间就找到了,所以查找效率大差不差,对于CPU而言,没什么差别,但是红黑树能具有更少的旋转!!!

一、红黑树概念

红黑树,是一种搜索二叉树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black,通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因为是接近平衡的。
在这里插入图片描述

二、红黑树性质

1.每个结点不是黑色就是红色
2.根结点是黑色
3.不存在连续两个红色的结点
4.每条路径的黑色结点数目相同
5.每个叶子都是黑色结点(指的是空结点NIL),加上NIL结点就是方便画图数路径

这样如何保证最长路径不超过最短路径的两倍呢?
极限最短:全黑
极限最长:一黑一红

三、红黑树 插入

在这里插入图片描述
现在要新插入结点,插入什么颜色?
如果是黑色,那么这一条路径的黑色路径和其他路径都不同了
如果是红色,那还好说,只有可能是该路径上的上一个和自己不同

红黑树被影响了之后有两个方向
1.变色
2.旋转

在这里插入图片描述
如果在27右边插入一个结点,那么27一定要先变黑,不然的话,就违背不能连续两个红,而此时如果不变27,而去变新插入的颜色,那又违背每条路径黑色结点数量相等的原理。

红黑树遇事不决看叔叔

新插入结点cur
新插入结点的父亲27parent
新插入结点的叔叔22uncle
父亲的父亲grandfather

①变色(c红 p红 g黑 u存在且红)

在这里插入图片描述

具象图:
在这里插入图片描述

第一步,parent变黑,uncle一起变黑,因为右边多了一个黑,uncle来平衡左右,然后grandfather变红,一直往上走,到为止,然后根变黑即可在这里插入图片描述
也就是说,这种情况只需要没条路径都多一个黑结点就可以了



有些情况单单变色解决不了
在这里插入图片描述
比如事先就有一个uncle结点是黑色,无法向这条路径增添多的黑,此时,就需要分情况来进行旋转了,需要注意的是新增添的结点一定是红,因为为了不破坏每条路径黑色结点数量相同的原理

②旋转(c红 p红 g黑 u存在且黑/不存在)

在这里插入图片描述
Ⅰ、如果u不存在,那么cur就是新增,因为cur一定是红,此时如果不是新增,就违背了不能两个连续红,不存在的时候旋转也不碍事
Ⅱ、uncle存在且为黑,这时cur一定为黑,那么现在cur为红的原因是子树因为颜色的调整,把cur变红

这时,uncle是黑色,父亲无法变色,不然的话就这条路径多了一个黑,所以这种情况就需要旋转:把grandfather旋转下来变成红色,parent变黑
在这里插入图片描述
子树中还有其他黑色结点,不用担心

③双旋转(c红 p红 g黑 u存在且黑/不存在)

同理,uncle不存在,cur即为新增,就算不存在,我旋转以下也不碍事
在这里插入图片描述

此时如果对p右单旋,再进行刚刚的变色操作的话,就会让右边多两个黑色结点,因为黑色结点在cur的字数中,而现在因为单旋让cur到了右树

所以这里的正确操作时需要双旋,首先将cur旋转到左树上去,也就是对p左单旋,转变为情况二进行单旋
在这里插入图片描述

四、旋转代码

//左旋
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;
	}
}

五、红黑树插入代码

bool Insert(const pair<K,V>&kv)
{
	if(_root==nullptr)
	{
		_root=new Node(kv);
		_root->col =BLACK;//enum,根结点为黑
		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);//传键值对new
	cur->_col=RED;//新插入一定是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;
		assert(grandfather&&grandfather0>_col==BLACK);
		//不为nullptr且不能和parent一起红
		Node*uncle;
		if(parent==grandfather->left)
		{
			uncle == grandfather->_right;
			if(uncle&&uncle->_col==RED)
			{//情况一,变色即可
				parent->_col=uncle->_col = BLACK;
				grandfather->_col = RED;
				//继续往上
				cur = grandfather;
				parent=cur->_parent;
			}
			else//情况二三
			{
				if(cur == parent->_left)
				{
					RotateR(grandfather);//右旋
					grandfather->_col=RED;
					parent->_col= BLACK;
				}
				else if(cur==parent->_right)
				{
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather ->_col= RED;
				}
			}
		}
		else if(parent==grandfather->_right)
		{
		//相同代码,只是换了一边
			uncle = grandfather->_left;
			if(uncle&&uncle->_col==RED)
			{
				parent->_col = 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 if(cur == parent->_left)
				{
					RotateR(parent);
					RotateL(grandfather);
					cur->_col=BLACK;
					grandfather->_col = RED;
				}
			}
		}
	}
	_root->_col = BLACK;
	return true;
}

六、红黑树平衡判断

红黑树最长路径不超过最短路径的二倍是颜色互斥得来的,所以判断颜色是否符合要求一定靠谱。

检查
1.每条简单路径的黑色结点数量相同
2.不能连续的红色结点
3.根结点是黑色

bool IsBalance()
{
	if(_root==nullptr)
		return true;
	if(_root->_col==RED)
	{
		cout<<"根结点不是黑色"<<endl;
		return false;
	}
	int benchMark=0;
	//黑色结点数量基准值,随便选一条
	Node*cur =_root;
	while(cur)
	{
		if(cur>_col == BLACK)
			++benchMark;
		cur = cur->_left;
	}
	return PrevCheck(_root,0,benchMark);
}

bool PrevCheck(Node*root,int blackNum,int benchMark)
{
	if(root==nullptr)
	{
		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);
}

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪皮兄弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值