红黑树 RBT

平衡二叉树

        任意一个节点,他的左子树最大深度与右子树最大深度不能超过1。

二叉排序树

        任何一个节点的左子节点都小于该节点,右子节点大于该节点。

AVL

        平衡二叉排序树,结合二者特点。

(上图中 0 、-1、 1为每个节点的平衡因子)

二叉排序树在添加节点的时候会改变左右子树的深度(平衡因子),就会打破平衡,同理删除也会,故引入了左旋右旋的方法来维持二叉树的平衡。

LL型 使用右旋平衡左右子树的绝对因子。

 ①将A的左孩子B提升为新的根结点;②将原来的根结点A降为B的右孩子;③各子树按大小关系连接(BL和AR不变,BR调整为A的左子树)。

具体实现还要看孩子节点和父亲节点

图中以A为根右旋 改变的是三个节点的父亲节点和孩子节点

三个字节点

A->left=E

B->right=A

X->left/right=B 

三个父亲节点

E->father=A

A->father=B

B->father=X

 代码如下


void RightRotate(BinaryTree*& rpRoot)
{
	if (nullptr == rpRoot || nullptr == rpRoot->pLeft)
	{
		return;
	}
	//标记不平衡结点	A
	BinaryTree* pMark = rpRoot;
	//标记不平衡结点的左	B
	BinaryTree* pTemp = rpRoot->pLeft;
	//X   pMark->pFather    E   pTemp->pRight
	//三个孩子
	//不平衡结点的左 -> 不平衡结点左的右   A->左 = E
	pMark->pLeft = pTemp->pRight;
	//不平衡结点左的右 -> 不平衡结点   B->右 = A
	pTemp->pRight = pMark;
	//不平衡节点是不是根
	if (pMark->pFather == nullptr)
	{
		//根   不平衡结点的左变为根
		rpRoot = pTemp;
	}
	else
	{
		//非根  判断不平衡结点是父亲的左还是右   (判断A是父亲的左还是右)
		if (pMark->pFather->pLeft == pMark)
		{
			//不平衡结点的父亲的左孩子 -> 不平衡结点的左  X->左 = B
			pMark->pFather->pLeft = pTemp;
		}
		else
		{
			//不平衡结点的父亲的右孩子 -> 不平衡结点的左  X->右 = B
			pMark->pFather->pRight = pTemp;
		}		
	}
	//三个父亲
	//不平衡结点左的右(已经变为平衡节点的左 A->左) 的父亲 -> 不平衡结点   E->父 = A
	//判断是否存在
	if (pMark->pLeft != nullptr)
	{
		pMark->pLeft->pFather = pMark;
	}
	//不平衡结点左的父亲-> 不平衡结点的父亲	B->父亲 = X
	pTemp->pFather = pMark->pFather;
	//不平衡结点的父亲-> 不平衡结点的左		A->父亲 = B
	pMark->pFather = pTemp;
}

 同理LR型也一样

 

 代码如下


void LeftRotate(BinaryTree*& rpRoot)
{
	if (nullptr == rpRoot || nullptr == rpRoot->pRight)
	{
		return;
	}
	
	BinaryTree* pMark = rpRoot;
	BinaryTree* pTemp = rpRoot->pRight;
	
	pMark->pRight = pTemp->pLeft;
	pTemp->pLeft = pMark;
	if (pMark->pFather == nullptr)
	{
		rpRoot = pTemp;
	}
	else
	{
		if (pMark->pFather->pLeft == pMark)
		{
			pMark->pFather->pLeft = pTemp;
		}
		else
		{
			pMark->pFather->pRight = pTemp;
		}
	}
	if (pMark->pRight != nullptr)
	{
		pMark->pRight->pFather = pMark;
	}
	pTemp->pFather = pMark->pFather;
	pMark->pFather = pTemp;
}

 了解到了这里就可以进一步认知红黑树了。

红黑树

        规则

1、树中所有的节点不是红的就是黑的。
2、根节点颜色必须是黑的。
3、所有的叶子节点的颜色都是黑色。
4、红色节点不能互为父子。
5、任意节点,从该节点到所有子孙节点路径上的黑色节点路径相同。

节点的添加

        初始节点为红色原因是不能打破规则5黑色节点数,如果添加节点为黑色,会改变路径上黑色节点总数,而插入红色节点可以根据他的父节点进行变换颜色来实现平衡。

然后再分析其父节点 若为黑色节点可直接添加,若为红色节点则需要判断其叔叔节点,都为红则将父亲叔叔节点置为黑色,爷爷节点置为红色,然后以爷爷节点作为标记再重新判断爷爷节点是否与爷爷的父亲节点同为红色,若父亲节点为红色,叔叔节点为黑色或者是叶子节点,则需要考虑该节点,父亲节点及爷爷节点的位置关系:

            (1)                        (2)                                    (3)                                (4)

1为爷爷节点 2为父亲节点 3为插入节点(标记节点)

如果有情况(1)则将其2节点左旋转化为(2)情况让后再整体右旋变色,这样做因为左旋和右旋不改变排序规则且将该排列的三个节点变成一黑为父亲节点二红为孩子节点使左右两边的黑色节点数相同。同理若父亲节点为爷爷节点的右孩子也是一样的做法。

获取父亲节点



RBT* getfather(RBT*&proot,int value )
{
	while (proot!=nullptr)
	{
		if(proot->value>value)
		{
			if(proot->pleft==nullptr)
				proot=proot->pleft;
			return proot;
		}
		else if(proot->value<value)
		{
			if(proot->pright==nullptr)
				proot=proot->pright;
			return proot;
		}
		else
		{
			cout<<"error"<<endl;
		break;
		}
			

	}
	return nullptr;
}

获取叔叔节点



RBT *getuncle(RBT* proot)
{
if(proot==nullptr||proot->pfather==nullptr||proot->pfather->pfather==nullptr)
	return nullptr;
if(proot->pfather=proot->pfather->pfather->pright)
	return proot->pfather->pfather->pleft;
else
	return proot->pfather->pfather->pright;


}

红黑树的添加



void addrbt(RBT* &proot,int value)
{
	RBT*pfather= getfather(proot,value);

	RBT*pmark=new RBT;
	pmark->color=red;
	pmark->pfather=pfather;
	pmark->pleft=nullptr;
	pmark->pright=nullptr;
	pmark->value=value;

	if(proot==nullptr)
	{
	proot=pmark;
	pmark->color=black;
	return;
	}

	if(pmark->value>pfather->value)
		pfather->pright=pmark;
	else
		pfather->pleft=pmark;

	while (true)
	{

		if(pmark->pfather->color==black)
			return;
		RBT*puncle= getuncle(pmark);

		if(puncle!=nullptr&& puncle->color==red)
		{
		pmark->pfather->color=black;
		puncle->color=black;
		puncle->pfather->color=red;

		if(puncle->pfather==nullptr)
		{
			puncle->pfather->color=black;
		break;
		}
		else
		{
			pmark=puncle->pfather;
			continue;
		}
		}
		if(puncle==nullptr||puncle->color==black)
		{
		if(pmark->pfather==pmark->pfather->pfather->pleft)
			{
				if(pmark=pmark->pfather->pright)
				{
					pmark=pmark->pfather;
					LeftRotate(pmark,proot);

				}
				pmark->color=red;
				pmark->pfather->color=black;
				pmark=pmark->pfather;
				RightRotate(pmark->pfather->pfather,proot);
				break;

			}
		if(pmark->pfather==pmark->pfather->pfather->pright)
			{
				if(pmark=pmark->pfather->pleft)
				{
					pmark=pmark->pfather;
					RightRotate(pmark,proot);

				}
				pmark->color=red;
				pmark->pfather->color=black;
				pmark=pmark->pfather;
				LeftRotate(pmark->pfather->pfather,proot);
				break;
			}

		}


	}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值