还在感觉为红黑树所困扰?相信我你就点进来看!!!★★★_红黑树好麻烦(1)

img
img

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

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

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

2.1 如果插入结点的父亲是黑结点:链接插入结点,结束
2.2 如果插入结点的父亲是红结点

2.2.1如果父亲是爷爷的左

2.2.1.1如果插入结点是父亲的左

2.2.1.1.1如果叔叔的颜色是红:
2.2.1.1.2如果叔叔的颜色是黑:
2.2.1.2如果插入结点是父亲的右

2.2.2如果父亲是爷爷的右

2.2.1.1如果插入结点是父亲的左
2.2.1.2如果插入结点是父亲的右

对于父亲结点颜色为红色时,如果此时父亲是爷爷的左,插入结点是父亲的左,分析如下。如果父亲结点的颜色是红色,那么爷爷结点的颜色一定是黑色。由于插入的结点和父亲的结点颜色都是红色,因此需要进行变色。那么是对插入结点进行变色还是对父亲结点进行变色呢?很显然是需要对父亲结点进行变色。为什么呢?如果是对插入结点变色,那么和直接插入黑色结点有什么区别呢?所示是要对父亲结点进行变色,即将父亲结点颜色变黑。如果父亲结点颜色变黑,那么该条路径就会多出一个黑色结点,因此需要将爷爷结点颜色变红,如果爷爷结点颜色变红,那么叔叔结点那条路径就会少一个黑色结点。然而,由于爷爷结点颜色是黑色,所以叔叔结点颜色可能是黑色,也可能是红色。所以此时我们就需要对叔叔结点的颜色进行讨论,即如果叔叔结点的颜色是黑色,如果叔叔结点的颜色是红色。如果叔叔结点的颜色是红色时,这种情况我们只需要将叔叔结点的红色变为黑色,这条路径黑色结点颜色就相同。至此,叔叔这条路径和父亲这条路径的黑色结点个数都保持了平衡。然而,由于我们将爷爷结点的颜色变成了红色,那么爷爷的父亲之前如果也是红色,是不是又不平衡了?所以,这里我们需要将爷爷为新插入的结点,重新进行讨论!!!总结来看,如果父亲节点颜色是红,父亲是爷爷的左,插入结点是父亲的左,叔叔结点是红色,我们需要将父亲变黑,爷爷变红,叔叔变黑,然后爷爷为新插入的结点,重新讨论!!!不过,我们想一下,我们在讨论叔叔的颜色时好像并没有发生旋转,仅仅是变色,与父亲是不是爷爷的左,插入结点是不是父亲的左无关啊,所以,在这里我们可以先对叔叔的结点颜色进行讨论,而后再讨论左右;因此,当父亲结点和叔叔结点都是红色时,可这样讨论“如果父亲和叔叔是红色,则父亲变黑,爷爷变红,叔叔变黑,然后爷爷变为新“插入”的结点,重新判断,这种情况我们有个需要注意的就是,如果爷爷是根的话,那么我们直接将爷爷变黑,直接结束即可,如下所示(插入3)(注意:只要不发生旋转,就不需要单独的讨论左还是右!!!):
在这里插入图片描述

1 插入的结点是根:根指向插入的结点,结点变黑,结束
2 插入的结点不是根

2.1 如果插入结点的父亲是黑结点:链接插入结点,结束
2.2 如果插入结点的父亲是红结点

2.2.1如果叔叔的颜色是红:父亲变黑,爷爷变红,叔叔变黑,然后爷爷为新插入的结点,重新讨论

2.2.2如果叔叔的颜色是黑

2.2.2.1如果父亲是爷爷的左

2.2.2.1.1如果插入结点是父亲的左
2.2.2.1.2如果插入结点是父亲的右

2.2.2.2如果父亲是爷爷的右

2.2.2.2.1如果插入结点是父亲的左
2.2.2.2.2如果插入结点是父亲的右

如上所示,这样讨论是不是显得更简单点呢?关于叔叔结点是红色的时候就讨论完了,下面我们看一下叔叔结点是黑色的情况。如果叔叔结点是黑色,那么显然我们就不能将叔叔结点变黑,因为叔叔本来就是黑色。所以,如果此时想要让叔叔这条路径多出一个黑结点,那么我们必须要发生旋转。因此,我们可以有这样一个结论:只有当父亲是红,叔叔是黑时才会发生旋转!!!只有发生旋转时我们才需要具体的讨论左右情况!!!。所以,现在我们就讨论一下左右情况;由于父亲是爷爷的左和父亲是爷爷的右这两种情况基本一样,只是旋转情况相反,因此我们只对父亲是爷爷的左这种情况进行详细讨论,另一种情况就自己研究研究吧,很简单。

对于父亲是红,叔叔是黑时,我们首先看一下如果父亲是爷爷的左,插入结点是父亲的左这种情况,如下图(插入3):
在这里插入图片描述

此时6是3的父亲,NULL是3的叔叔,这里我们可能会有一个疑问,为什么叔叔是NULL?在此种情况,叔叔只可能是NULL,如果叔叔是黑且不是NULL,在3插入之前6一定是叶子结点,然而6的兄弟如果不是NULL,这颗树肯定不会平衡,因此此时6的兄弟一定是NULL。如果此时我们将父亲变黑,父亲这条路径会多出一个黑结点,那么就将爷爷变红,父亲这条路径就少一个黑结点;然而,由于爷爷是父亲和叔叔的公共节点,因此叔叔这条路径同样少了一个黑色结点。如果我们想让叔叔这条路径不少黑色结点,我们应该很自然的就能想到将父亲节点6变成公共黑结点,即右旋,这样这两条路径都不少黑结点,由于黑色结点成为了公共节点,我们此时也不必担心黑结点的父亲是不是红了,因此这样调整后这课红黑树就平衡了,总的调节过程如下:父亲变黑,爷爷变红,以爷爷为支点右旋,如下图所示:
在这里插入图片描述对于父亲是红,叔叔是黑时,我们首先看一下如果父亲是爷爷的左,插入结点是父亲的右这种情况,如下图(插入9):
在这里插入图片描述此种情况,我们观察一下,应该就不难看出。如果我们以父亲为支点进行左旋,这时这个情况就和上种情况一样了,即插入结点是父亲的左,父亲是爷爷的左?所以,对于此种情况,我们只需要进行一下左旋,然后即可将此种情况变换为上一种情况,然后重新判断即可。不过,我们需要注意的是,在进行左旋时,是以父亲为支点左旋,并且要将父亲结点作为新插入的结点重新判断,总结就是(父为新插入的结点,以父亲为支点左旋,重新判断)如下图所示:
在这里插入图片描述以上所有情况都以列出,并进行了详细的讨论,下面综合一下各种情况如下:

1 插入的结点是根:根指向插入的结点,结点变黑,结束
2 插入的结点不是根

2.1 如果插入结点的父亲是黑结点:链接插入结点,结束
2.2 如果插入结点的父亲是红结点

2.2.1如果叔叔的颜色是红:父亲变黑,爷爷变红,叔叔变黑,然后爷爷为新插入的结点,重新讨论

2.2.2如果叔叔的颜色是黑

2.2.2.1如果父亲是爷爷的左

2.2.2.1.1如果插入结点是父亲的左:爷爷变红,父亲变黑,爷爷为支点右旋,结束
2.2.2.1.2如果插入结点是父亲的右:父为新Z,父为支点左旋,重新判断

2.2.2.2如果父亲是爷爷的右

2.2.2.2.1如果插入结点是父亲的左:父为新Z,父为支点右旋,重新判断
2.2.2.2.2如果插入结点是父亲的右:爷爷变红,父亲变黑,爷爷为支点左旋,结束

注意

1 只有当父亲是红,叔叔是黑时才会发生旋转!!!
2 只有发生旋转时我们才需要具体的讨论左右情况!!!
3 如果父亲是红,叔叔是红时,在我们变色后重新判断之前,需要先判断爷爷是不是根,如果爷爷是根,那么将爷爷变黑结束即可。
4 在进行结点插入时,由于一个结点包含三个指针,即左右孩子指针和父指针,所千万不要忘记链接父亲指针。

代码如下
#include <stdio.h>
#include <stdlib.h>
enum COLOR{RED,BLACK};
typedef struct tree
{
	int nValue;
	enum COLOR color; 
	struct tree \*pfather;
	struct tree \*plchild;
	struct tree \*prchild;
}BinaryTree;

void RightRotaryTree(BinaryTree \*pTree,BinaryTree \*\*pRoot)
{
	if(pTree == NULL || pTree->plchild == NULL) return;

	BinaryTree \*pRotaryChild = pTree->plchild;
	BinaryTree \*pRotaryGS = pTree->plchild->prchild;
	BinaryTree \*pRotaryFather = pTree->pfather;
	//旋转的点的左孩子重新连接
	pTree->plchild = pRotaryGS;
	if(pRotaryGS != NULL)
	{
		pRotaryGS->pfather = pTree;
	}
	//旋转点的左孩子
	pRotaryChild->prchild = pTree;
	pTree->pfather = pRotaryChild;
	//如果有父亲,连接旋转点的父亲
	pRotaryChild->pfather = pRotaryFather;
	if(pRotaryFather != NULL)
	{
		if(pRotaryFather->plchild == pTree)
		{
			pRotaryFather->plchild = pRotaryChild;
		}
		else
		{
			pRotaryFather->prchild = pRotaryChild;
		}
	}
	else//旋转的是根结点时换根
	{
		\*pRoot = pRotaryChild;
	}
}
void LeftRotaryTree(BinaryTree \*pTree,BinaryTree \*\*pRoot)
{
	if(pTree == NULL || pTree->prchild == NULL) return;

	BinaryTree \*pRotaryChild = pTree->prchild;
	BinaryTree \*pRotaryGS = pTree->prchild->plchild;
	BinaryTree \*pRotaryFather = pTree->pfather;
	//旋转的点的左孩子重新连接
	pTree->prchild = pRotaryGS;
	if(pRotaryGS != NULL)
	{
		pRotaryGS->pfather = pTree;
	}
	//旋转点的左孩子
	pRotaryChild->plchild = pTree;
	pTree->pfather = pRotaryChild;
	//如果有父亲,连接旋转点的父亲
	pRotaryChild->pfather = pRotaryFather;
	if(pRotaryFather != NULL)
	{
		if(pRotaryFather->plchild == pTree)
		{
			pRotaryFather->plchild = pRotaryChild;
		}
		else
		{
			pRotaryFather->prchild = pRotaryChild;
		}
	}
	else//旋转的是根结点时换根
	{
		\*pRoot = pRotaryChild;
	}
}
BinaryTree\* SearchNode(BinaryTree \*pRoot,int nValue)
{
	if(pRoot == NULL) return NULL;

	BinaryTree \*pFather = pRoot;
	BinaryTree \*pNode = pRoot;
	while(pFather)
	{
		pNode = pFather;
		if(pFather->nValue < nValue && pFather->prchild)
			pFather = pFather->prchild;
		else if(pFather->nValue > nValue)
			pFather = pFather->plchild;
		else
			return pFather;
	}
	return pNode;
}
//获取叔叔结点
BinaryTree \*GetUncle(BinaryTree \*pFather)
{
	BinaryTree \*pGrandFather = pFather->pfather;
	if(pGrandFather == NULL)
		return NULL;
	else if(pGrandFather->plchild == pFather)
		return pGrandFather->prchild;
	else
		return pGrandFather->plchild;
}
//获取兄弟结点
BinaryTree \*GetBrother(BinaryTree \*pFather,BinaryTree \*pNode)
{
	return pFather->plchild != pNode ? pFather->plchild : pFather->prchild;
}
//插入红黑树的结点
void InsertRBTNode(BinaryTree \*pTree,BinaryTree \*\*pRoot,int nValue)//Z为新插入的结点
{
	//为新插入的结点申请空间
	BinaryTree \*pTemp = (BinaryTree\*)malloc(sizeof(BinaryTree));
	pTemp->color = RED;
	pTemp->nValue = nValue;
	pTemp->plchild = NULL;
	pTemp->prchild = NULL;
	pTemp->pfather = NULL;

	//树为空:直接插入,并且根变黑
	if(\*pRoot == NULL)
	{
		\*pRoot = pTemp;
		(\*pRoot)->color = BLACK;
		return;
	}
	//查找插入结点位置
	BinaryTree \*pFather = SearchNode(\*pRoot,nValue);
	//如果插入的结点相同
	if(pFather->nValue == nValue)
		return;
	//直接插入
	if(nValue < pFather->nValue)
		pFather->plchild = pTemp;
	else
		pFather->prchild = pTemp;
	//链接父亲
	pTemp->pfather = pFather;

	//父亲结点是黑色:结束
	if(pFather->color == BLACK)
		return;
	//父亲结点是红色
	BinaryTree \*pGrandFather = pFather->pfather;
	BinaryTree \*pUncle = GetUncle(pFather);
	while(pFather->color == RED)
	{
		//叔叔红:父变黑,叔叔变黑,爷爷变红,爷爷为新Z,爷爷非根重新判断
		if(pUncle && pUncle->color == RED)
		{
			pFather->color = BLACK;
			pUncle->color = BLACK;
			pGrandFather->color = RED;
			pTemp = pGrandFather;
			//如果爷爷是根
			if(pTemp == \*pRoot)
			{
				pTemp->color = BLACK;
				return;
			}
			//更新父子关系
			pFather = pTemp->pfather;
			pUncle = GetUncle(pFather);
			pGrandFather = pFather->pfather;

			continue;
		}
		//叔叔黑(叔叔黑才发生旋转,另外叔叔黑可能是空)
		else
		{
			//爷爷为空:父亲变黑,结束
			if(pGrandFather == NULL)
			{
				pFather->color = BLACK;
				return;
			}
			//爷爷非空
			else
			{
				//父亲是爷爷的左
				if(pFather == pGrandFather->plchild)
				{
					//Z是父亲的左:爷爷变红,父亲变黑,爷爷为支点右旋,结束
					if(pTemp == pFather->plchild)
					{
						pGrandFather->color = RED;
						pFather->color = BLACK;
						RightRotaryTree(pGrandFather,pRoot);
						return;
					}
					//Z是父亲的右:父为新Z,父为支点左旋,重新判断
					else
					{
						pTemp = pFather;
						LeftRotaryTree(pTemp,pRoot);
						//更新父子关系
						pFather = pTemp->pfather;
						pGrandFather = pFather->pfather;
						pUncle = GetUncle(pFather);
						continue;
					}
				}
				//父亲是爷爷的右
				else
				{
					//Z是父亲的左:父为新Z,父为支点右旋,重新判断
					if(pTemp == pFather->plchild)
					{
						pTemp = pFather;
						RightRotaryTree(pTemp,pRoot);
						//更新父子关系
						pFather = pTemp->pfather;
						pGrandFather = pFather->pfather;
						pUncle = GetUncle(pFather);
						continue;
					}
					//Z是父亲的右:爷爷变红,父亲变黑,爷爷为支点左旋,结束
					else
					{
						pGrandFather->color = RED;
						pFather->color = BLACK;
						LeftRotaryTree(pGrandFather,pRoot);
						return;
					}
				}
			}
		}
	}
}

//删除结点
void DeleteRBTNode(BinaryTree \*pTree,BinaryTree \*\*pRoot,int nValue)
{
	if(\*pRoot == NULL) return;
	//查找要删除的结点,并转换(真正要删除的只有一个孩子或无孩子)
	BinaryTree \*pNode = SearchNode(pTree,nValue);
	BinaryTree \*pMark = pNode;//标记要删除的结点
	//如果没找到,则直接返回
	if(pNode == NULL || pNode->nValue != nValue)
	{
		printf("There don't have the nValue int the tree.\n");
		return;
	}
	//如果找到了则将结点转换:转换成真正要删除的结点
	BinaryTree \*pFather = pNode->pfather;
	//寻找左边最右替换删除结点
	if(pNode->plchild && pNode->prchild)//有两个孩子才进行转换
	{
		pNode = pNode->plchild;
		while(pNode->prchild)
			pNode = pNode->prchild;
		pMark->nValue = pNode->nValue;
	}
	//如果删除的是根
	if((\*pRoot)->nValue == nValue)
	{
		//如果根无孩子:直接删除,结束
		if((\*pRoot)->plchild == NULL && (\*pRoot)->prchild == NULL)
		{
			free(\*pRoot);
			\*pRoot = NULL;
			return;
		}
		//如果根有一个孩子:直接删除,红孩子成为新根,根变黑,结束
		if(((\*pRoot)->plchild == NULL && (\*pRoot)->prchild != NULL) || ((\*pRoot)->plchild != NULL && (\*pRoot)->prchild == NULL))
		{
			BinaryTree \*pDel = \*pRoot;
			\*pRoot = (\*pRoot)->plchild == NULL? (\*pRoot)->prchild:(\*pRoot)->plchild;
			(\*pRoot)->color = BLACK;
			(\*pRoot)->pfather = NULL;
			free(pDel);
			pDel = NULL;
			return;
		}
	}
	//-----------下面删除的是非根的情况-------------------(因为上面判断的就是删除的是根的情况,因此走下来就一定是非根)
	//如果删除的是红结点:直接删除,父亲指向空,结束
	pFather = pNode->pfather;
	//printf("pFather[%d]\n",pFather->nValue);
	if(pNode->color == RED)
	{
		if(pFather->plchild == pNode)
			pFather->plchild = NULL;
		else 
			pFather->prchild = NULL;

		printf("pNode[%d]\n",pNode->nValue);
		free(pNode);
		pNode = NULL;


![img](https://img-blog.csdnimg.cn/img_convert/c1b2fc1f4e5bfcc4af4babe7977bdcf3.png)
![img](https://img-blog.csdnimg.cn/img_convert/324e1d3312db229d9823c7b3405f1fbe.png)

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

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

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

面判断的就是删除的是根的情况,因此走下来就一定是非根)
	//如果删除的是红结点:直接删除,父亲指向空,结束
	pFather = pNode->pfather;
	//printf("pFather[%d]\n",pFather->nValue);
	if(pNode->color == RED)
	{
		if(pFather->plchild == pNode)
			pFather->plchild = NULL;
		else 
			pFather->prchild = NULL;

		printf("pNode[%d]\n",pNode->nValue);
		free(pNode);
		pNode = NULL;


[外链图片转存中...(img-AjGUFAUK-1715889888524)]
[外链图片转存中...(img-xF5shtBJ-1715889888524)]

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

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

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值