红黑树-添加-详细讲解-只需三步你就能搞定红黑树的添加-需要有BST和旋转的知识-只讲红黑树-不讲BST和旋转。

在这里插入图片描述

代码:

/*
*	作者:流放的未来
*	时间:2020/10/29/8/43
*	红黑树定义:
*		1. 根必须为黑。
*       2. 树上节点非红即黑。
*       3. 两个节点不能同时为红色。
*       4. 路径上的黑节点个数一致。
*       5. 所有空节点,默认为黑,俗称黑哨兵。
*/
//用到C语言的输入输出等基础函数因此需要			stdio
//需要malloc申请地址空间						stdlib
#include<stdio.h>
#include<stdlib.h>
//定义红黑树的结构体,保存红黑树节点的信息
typedef struct treeNode
{
	int nValue;
	int nColor;
	struct treeNode *pLeft;
	struct treeNode *pRight;
	struct treeNode *pFather;
}RBT;

RBT *pRBT = NULL;
//设置枚举更容易操作,让代码更加清晰,实际上RED=0,BLACK=1。
enum COLOR{RED,BLACK};
//分析所需函数
//首先添加:需要什么,按照我们分析的步骤
/*
*   一、有根吗?:
	1. 没有好办,直接把现在的点变成根,根指针赋值当前节点,
*	当前节点如何的得到,因此得出需要一个RBT *GetRBTNode(int nValue)的节点
* (初始化节点颜色为红,数值可以给定或者数组,这里选择数组,因为我们主要为了构建RBT)。
*	2. 有根。
*		二、父亲颜色是啥?因此发现我们需要得到父亲的节点,我们结构体定义了找到父亲的指针,因此不需要函数。
*			1. 黑色:直接添加。
*			2. 红色:
*				三、叔叔啥颜色:因此需要有个得到叔叔的函数RBT *GetUncle(RBT *pNode);
*					1. 红色:父亲和叔叔同时变黑,爷爷变红,标记转移到爷爷,继续讨论,;
*                           (当前无法解决,向上寻求帮助)(因此可以看出需要将三的判断做成循环)
*					2. 黑色:判断父亲位置?:直接可以得到不需要做成函数。
*						四、1. 父亲在左:标记位置?需要旋转,因此需要做出旋转函数
*							五、判断孩子也就是标记位置
*								1. 父亲的左:父亲变黑,爷爷变红,以爷爷为支点右旋,注意父亲可能会变成根。
*								2. 父亲的右:以父亲为支点左旋,标记给父亲(变化树结构转化成我们可以解决的)
*						四、2. 父亲在右:标记位置?
*							五、判断孩子也就是标记位置
*								1. 父亲的左:右旋,标记给父亲(变成我们可以修改的结构)
*								2. 父亲的右:父亲变黑,爷爷变红,左旋,注意父亲可能会成为根
*/

//通过分析得出以下所需函数
RBT *GetRBTNode(int nValue);
void CreateRBT(int arr[], int nLength);
void AddRBTNode(int nValue);
RBT *GetUncle(RBT *pNode);
void InorderTraversal();
void BalanceREDBALCK(RBT *pNode);
void RightRotation(RBT **ppTree);
void LeftRotation(RBT **ppTree);

void CreateRBT(int arr[], int nLength)
{
	//边界判断
/*
*	建议编程方式:
*		1. 边界判断。准备功能测试用例,可以先定义个变量,设计到一个设计一个用例,用于测试。
*		2. 变量申请。想好当前可能需要的变量放到边界判断前,集体定义,便于观看。
*		3. 初始化。永远记得边界判断完立马初始化,别管有没有用,养成习惯。
*/
//------------------------定义阶段:---------------------------------------------------
	int i;
//----------------------------边界判断阶段:--------------------------------------------
	if(NULL==arr || nLength<=0)return;
//------------------------------------------初始化阶段:--------------------------------
	i = 0;
//------------------具体流程阶段:------------------------------------------------------
	for(i=0; i<nLength; i++)
	{
		AddRBTNode(arr[i]);
	}
}

void AddRBTNode(int nValue)
{
	RBT *pNode = GetRBTNode(nValue);
	RBT *pTemp = NULL;
	if(pRBT == NULL)
	{
		BalanceREDBALCK(pNode);
		return;
	}

	pTemp = pRBT;
	while(pTemp)
	{
		if(nValue > pTemp->nValue)
		{
			if(pTemp->pRight == NULL)
			{
				pTemp->pRight = pNode;
				pNode->pFather = pTemp;
				BalanceREDBALCK(pNode);
				return;
			}
			pTemp = pTemp->pRight;
		}
		else if(nValue < pTemp->nValue)
		{
			if(pTemp->pLeft == NULL)
			{
				pTemp->pLeft = pNode;
				pNode->pFather = pTemp;
				BalanceREDBALCK(pNode);
				return;
			}
			pTemp = pTemp->pLeft;
		}
		else
		{
			printf("构造错误,不允许出现重复");
			exit(1);
		}
	}
}
//注意,只要标记转移就是重新开始判断的情况,只要旋转就需要考虑根,
//具体需不需要在分析,但是一定要记得旋转看根,根是最容易被忽略的地方。
void BalanceREDBALCK(RBT *pNode)
{
	//-------------------------申请父亲,叔叔,爷爷节点。-------------------------
	RBT *pFather;
	RBT *pUncle;
	RBT *pGrandFather;
	//--------------------------------节点不存在不用调整-------------------------
	if(pNode == NULL)return;
	//-------------------------一、判断根是否存在
	//-----------------------------1. 根不存在的情况-----------------------------
	if(NULL == pNode->pFather)
	{
		pNode->nColor = BLACK;
		pRBT = pNode;
		return;
	}
	else
	{
		//-------------------------2. 根存在-------------------------------------
		//----------------------二、判断父亲颜色-------------------------
		while(pNode != NULL)
		{
			//-------------------------初始化父亲和叔叔-------------------------
			pFather = pNode->pFather;
			pUncle = GetUncle(pNode);
			//-------------------------不是根肯定有父亲,因从不需要判断父亲是否存在-------------------------
			//----------------------1. 父亲黑色,不需要调整-------------------------
			if(BLACK==pFather->nColor)
			{
				return;
			}
			//----------------------2. 父亲红色,孩子红色,需要调整-------------------------
			else
			{
				//-------------------------父亲红色,爷爷肯定存在而且是黑色-------------------------
				pGrandFather = pFather->pFather;
				//----------------三、判断叔叔颜色-------------------------
				//-------------------------叔叔不一定会存在,记得判空-------------------------
				//--------------------1. 叔叔红色-------------------------
				if(pUncle!=NULL && RED==pUncle->nColor)
				{
					//----------------叔叔变黑,父亲变黑,爷爷变红,标记给爷爷,注意爷爷是根的情况
					pUncle->nColor = BLACK;
					pFather->nColor = BLACK;
					pGrandFather->nColor = RED;
					pNode = pGrandFather;
					//-------------------------爷爷是根吗-------------------------
					if(pNode->pFather == NULL)
					{
						pNode->nColor = BLACK;
						pRBT = pNode;
						return;
					}
					//-------------------------重新讨论-------------------------
					continue;
				}
				else
				{
					//----------------2. 叔叔黑色-------------------------
					//--------------四、父亲位置
					//----------------1. 左侧
					if(pGrandFather->pLeft == pFather)
					{
						//----------五、孩子位置,也就是标记位置-------------------------
						//--------------1.左侧,父亲变黑,爷爷变红,右旋,注意根的情况-------------------------
						if(pFather->pLeft == pNode)
						{
							pFather->nColor = BLACK;
							pGrandFather->nColor = RED;
							RightRotation(&pGrandFather);
							return;
						}
						//-------------2. 右侧,左的右,左旋,变成左的左
						else
						{
							LeftRotation(&pFather);
							pNode = pFather->pLeft;
						}
					}
					//-------------2. 右侧,-------------------------
					else
					{
						//------五、标记的位置-------------------------
						//----------1.父亲的右侧,右的右,父亲变黑,爷爷变红,左旋,注意根
						if(pFather->pRight == pNode)
						{
							pFather->nColor = BLACK;
							pGrandFather->nColor = RED;
							LeftRotation(&pGrandFather);
							return;
						}
						//----------2. 左侧,右的左,右旋,变成右的右
						else
						{
							RightRotation(&pFather);
							pNode = pFather->pRight;
						}
					}
				}
			}
		}
	}
}

RBT *GetUncle(RBT *pNode)
{
	if(pNode==NULL || pNode->pFather==NULL || pNode->pFather->pFather==NULL)return NULL;
	if(pNode->pFather == pNode->pFather->pFather->pLeft)
		return pNode->pFather->pFather->pRight;
	return pNode->pFather->pFather->pLeft;
}

RBT *GetRBTNode(int nValue)
{
	RBT *pNode = (RBT *)malloc(sizeof(RBT));//malloc 申请地址,返回的是void *泛型指针
	//可以调用memset(pNode, 0, sizeof(RBT)),除了nValue其他的都不需要了
	pNode->nValue = nValue;
	pNode->nColor = RED;
	pNode->pFather = NULL;
	pNode->pLeft = NULL;
	pNode->pRight = NULL;
	return pNode;
}

void RightRotation(RBT **ppTree)
{
	RBT *pLeft = NULL;
	RBT *pFather = NULL;
	if(*ppTree==NULL || (*ppTree)->pLeft==NULL)return;
	pFather = *ppTree;
	pLeft = (*ppTree)->pLeft;

	pFather->pLeft =  pLeft->pRight;
	pLeft->pRight = pFather;
	if(pFather->pFather == NULL)
	{
		pRBT = pLeft;
		pLeft->nColor = BLACK;
	}
	else if(pFather->pFather->pLeft == pFather)
		pFather->pFather->pLeft = pLeft;
	else
		pFather->pFather->pRight = pLeft;
	
	if(pFather->pLeft != NULL)
	{
		pFather->pLeft->pFather = pFather;
	}

	pLeft->pFather = pFather->pFather;
	pFather->pFather = pLeft;
	
	*ppTree = pLeft;
}

void LeftRotation(RBT **ppFather)
{
	RBT *pRight = NULL;
	RBT *pFather = *ppFather;
	if(pFather==NULL || pFather->pRight==NULL)return;
	pRight = pFather->pRight;
	pFather->pRight =  pRight->pLeft;
	pRight->pLeft = pFather;

	if(pFather->pFather == NULL)
	{
		pRBT = pRight;
		pRBT->nColor = BLACK;
	}
	else
	{
		if(pFather->pFather->pLeft == pFather)
			pFather->pFather->pLeft = pRight;
		else
			pFather->pFather->pRight = pRight;
	}

	if(pFather->pRight != NULL)
		pFather->pRight->pFather = pFather;
	pRight->pFather = pFather->pFather;
	pFather->pFather = pRight;
	
	*ppFather = pRight;
}

void InorderTraversal(RBT *pRBT)
{
	if(pRBT== NULL)return;
	InorderTraversal(pRBT->pLeft);
	printf("数值%d\n",pRBT->nValue);
	printf("颜色%d\n",pRBT->nColor);
	InorderTraversal(pRBT->pRight);
}


int main()
{
	int arr[] = {11,2,7,4,5,12,14};
	CreateRBT(arr,sizeof(arr)/sizeof(arr[0]));
	InorderTraversal(pRBT);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值