2024年最全红黑树(C++实现)_红黑树存kv,2024年互联网大厂C C++笔经

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

{
RBTreeNode<K, V> _left;//结点的左孩子
RBTreeNode<K, V> _right;//结点的右孩子
RBTreeNode<K, V> _parent;//结点的双亲
pair<K, V>_kv;
Color _color;//该结点的颜色
RBTreeNode(const pair<K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_color(RED)
{}
};


## 红黑树的插入



> 
> 那么我们插入结点时选择插入黑结点还是红结点呢?
> 
> 
> 


当然是选择插入红结点了。选择插入黑结点那麻烦就大了,那1条路径上就多了1个黑结点,破坏了性质4,代价很大。插入红结点,如果它的父亲结点是黑色则不用调整,拍拍屁股走人,它的父亲是红色那我们在进行后序的处理。


**总结一下:**  
 1.插入黑色结点一定破坏性质4,调整起来会很麻烦  
 2.插入红结点不一定破坏红黑树的性质,它的父亲结点是红色才进行调整,比插入黑结点调整起来方便。  
   
 ✨✨✨✨✨✨✨✨✨✨\*\*\*我是分割线\*\*\*✨✨✨✨✨✨✨✨✨✨✨✨✨✨



> 
> 插入的逻辑:
> 
> 
> 


1.找到插入结点的位置  
 2.插入结点  
 3.检测新结点插入后是否破坏了红黑的性质,如果破坏则需要进行处理  
 


因为新插入结点的颜色是红色,若它的父亲结点是黑色不用调整,是红色的话需要对红黑树分情况来讨论。



> 
> 红黑树调整主要看叔叔结点
> 
> 
> 


下面我们根据叔叔结点的情况来具体看一下。


### 情况一


以下用p来代表parent结点,c代表cur为新增结点,g代表grandparent结点,u代表uncle结点。  
 我们还是跟AVL树一样画具象图:


1.叔叔结点存在且为红  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/cd1b12cd21da42cab19655f0bc2e9e00.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)  
 为什么把g变成红色呢?如果g不变成红色,那此时子树上就多了1个黑结点了。  
 只要我们画出具象图,那面试时手撕红黑树也完全不怂。


当然还有很多种情况,那就给出抽象图:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/1032e74e2faf4c56822753db24a587f1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)  
 这种情况下cur在p的左边还是右边都不影响。


### 情况二



> 
> 叔叔结点存在且为黑,新增结点是p的左边
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/06d4a34988bb45079b4c75fa3c8f5290.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)  
 这是由情况一变来的,如果u存在那cur一定是黑的,不是新郑结点,这样才满足红黑树的性质。


✨✨✨✨✨✨✨✨✨✨\*\*\*我是分割线\*\*\*✨✨✨✨✨✨✨✨✨✨✨✨✨✨



> 
> 新增结点是p的右边
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/92109928e6344233ba37fe911f70b834.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)


### 情况三



> 
> 叔叔结点不存在
> 
> 
> 


**新增结点在parent的左边**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/a578ee54753843b582642a808f1a968c.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)  
 **新增结点在parent的右边**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/422073c114c646279c8835e641853d18.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)  
 总结一下:  
   
 以上的情况都是父亲结点在祖先结点的左边,在祖先结点的右边也是相同的处理方法  
 1.叔叔结点存在且为红,把父亲结点和叔叔结点变黑,祖先变红继续向上处理直到祖先是根节点  
 2.叔叔存在为黑,祖孙三代在一条直线上进行单旋,不在则进行双旋  
 3.叔叔不存在,祖孙三代在一条直线上进行单旋,不在则进行双旋  
 所以2,3的逻辑可以合在一起,分为新增结点在父亲结点的左边还是右边处理。  
 


**下面再来简单的说说父亲结点在祖先结点的右边**



> 
> 叔叔存在且为红
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/77971440be024919916fa148add7b9a1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)  
 此时,cur在p的左边还是右边没有影响。



> 
> 叔叔存在且为黑
> 
> 
> 


**新增结点在父亲结点的右边**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/58c5fd873ca245759977a956e928550b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)  
 **新增结点在父亲结点的左边**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/bad000af9df34109ba0230071c951fa9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)



> 
> 叔叔不存在
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/49e86ae5c71240e9b79d9cb27fce9a27.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAX0VuZOS4tuaWreW8pg==,size_20,color_FFFFFF,t_70,g_se,x_16)  
 总结一下:  
   
 1.叔叔存在且为红,u,p变黑,g变红继续向上调,直到g为根结点,最后把g变黑  
 2.叔叔存在且为黑,祖孙3带在一条直线上单旋,折线要双旋  
 3.叔叔不存在,祖孙3带在一条直线上单旋,折线要双旋


✨✨✨✨✨✨✨✨✨✨\*\*\*我是分割线\*\*\*✨✨✨✨✨✨✨✨✨✨✨✨✨✨


代码如下:



pair<Node\*, bool> insert(const pair<K, V>& kv)
{
	//1.树为空
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_color = BLACK;//根结点为黑色
		return make\_pair(_root, true);
	}
	//树不为空
	Node\* cur = _root;
	Node\* parent = nullptr;
	while (cur)
	{
		//新结点key大于当前结点往右边
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		//新结点key小于当前结点往左边
		else if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return make\_pair(cur, false);
		}
	}
	cur = new Node(kv);
	Node\* newnode = cur;
	newnode->_color = RED;
	if (parent->_kv.first < kv.first)
	{
		parent->_right = newnode;
		newnode->_parent = parent;
	}
	else
	{
		parent->_left = newnode;
		newnode->_parent = parent;
	}

	//开始调整颜色
	//父亲存在且为红
	while (parent && parent->_color == RED)
	{
		Node\* grandParent = parent->_parent;		
		//parent是grandParent左孩子
		if (grandParent->_left == parent)
		{
			Node\* uncle = grandParent->_right;
			//叔叔存在且为红色,父亲和叔叔都调为黑色
			//祖先调为红色,如果不调那每条路径的黑结点变了
			if (uncle && uncle->_color == RED)
			{
				parent->_color = BLACK;
				uncle->_color = BLACK;
				grandParent->_color = RED;
				//继续往上调
				cur = grandParent;
				parent = cur->_parent;
			}
			else//叔叔不存在或叔叔存在且为黑
			{
				if (parent->_left == cur)
				{    //右单旋
					RotateR(grandParent);
					parent->_color = BLACK;
					grandParent->_color = RED;
				}
				else //parent->\_right == cur
				{
					RotateL(parent);
					RotateR(grandParent);
					grandParent->_color = RED;
					cur->_color = BLACK;
				}
				break;
			}
		}
		else //parent是grandParent左孩子
		{
			Node\* uncle = grandParent->_left;
			if (uncle && uncle->_color == RED)
			{
				uncle->_color = BLACK;
				parent->_color = BLACK;
				grandParent->_color = RED;
				cur = grandParent;
				parent = cur->_parent;
			}
			else
			{
				if (parent->_right == cur)
				{
					RotateL(grandParent);
					parent->_color = BLACK;
					grandParent->_color = RED;
				}
				else
				{
					RotateR(parent);
					RotateL(grandParent);
					cur->_color = BLACK;
					grandParent->_color = RED;
				}
				break;
			}	
		}
	}
	_root->_color = BLACK;
	return make\_pair(newnode, true);
}
void RotateL(Node\* parent)
{
	Node\* subR = parent->_right;
	Node\* subRL = subR->_left;
	Node\* parentParent = parent->_parent;
	//先旋转
	parent->_right = subRL;
	subR->_left = parent;

	parent->_parent = subR;
	//在改父亲结点
	if (subRL)
		subRL->_parent = parent;
	if (_root == parent)
	{
		_root = subR;
		_root->_parent = nullptr;
	}

	else
	{
		//subR旋转后可能是左右子树2种情况
		if (parentParent->_left == parent)
			parentParent->_left = subR;
		else
			parentParent->_right = subR;
		subR->_parent = parentParent;
	}
}
void RotateR(Node\* parent)
{
	Node\* subL = parent->_left;
	Node\* subLR = subL->_right;
	Node\* parentParent = parent->_parent;//记录parent的父亲结点
	//subLR做parent->\_left
	parent->_left = subLR;
	subL->_right = parent;
	//同时更新动的2个节点的parent
	//注意subLR还可能是空结点
	if (subLR)
		subLR->_parent = parent;
	parent->_parent = subL;
	//parent可能是单独的树,或者子树,分情况
	if (_root == parent)
	{

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

独的树,或者子树,分情况
if (_root == parent)
{

[外链图片转存中…(img-KmcmQAie-1715617960148)]
[外链图片转存中…(img-QaDBLnip-1715617960149)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值