第102篇 C++数据结构(十二)红黑树的删除

红黑树删除的结点孩子情况

红黑树的删除和普通的二叉搜索树的删除时一样的,都是存在三种情况:
1.删除的结点没有孩子,这时这个结点可以直接删除
2.删除的结点有一个孩子,则用孩子的数据来替换掉它的数据,像数组的删除用后面的数据把前面的数据覆盖掉一样,然后删除该结点
3.删除的结点有两个孩子,这种情况可以转成(1),(2)情况,就是找前驱结点或者后继结点来删除,前驱结点是该结点左子树值最大的结点,后继结点时右子树值最小的结点,这两个结点最多只有一个孩子

红黑树的删除代码实现

和普通搜索树一样,大于往后边,小于往左边

void remove(RBTNodePtr node, _DataType value)
{
	if (!node)
	{
		return;
	}
	else
	{
		if (value == node->m_value)
		{
			RBTNodePtr rmNode = node;//要删除的结点
			if (node->m_leftChild && node->m_rightChild)//如果要删除的结点有左右孩子,则找右子树中最小值的结点替换
			{
				rmNode = node->m_rightChild;//首先第一个替换对象是右孩子
				while (rmNode->m_leftChild)//如果右孩子有左孩子
				{
					rmNode = rmNode->m_leftChild;//则换成当前结点的左孩子
				}
				node->m_value = rmNode->m_value;//更新node的值(替换)
			}

			RBTNodePtr parent = rmNode->m_parent;//待删除结点的父结点
			RBTNodePtr replaceNode = (rmNode->m_leftChild) ? rmNode->m_leftChild : rmNode->m_rightChild;//用孩子替换
			if (replaceNode)//如果有孩子,即替换者不为空
			{
				replaceNode->m_parent = parent;//更新孩子的父结点
			}

			if (parent)//如果父结点不为空,则更新父结点的孩子情况,将替换者替换上去
			{
				(rmNode == parent->m_leftChild) ? parent->m_leftChild = replaceNode: parent->m_rightChild = replaceNode;
			}
			else
			{
				m_root = replaceNode;//否则,更新根结点
			}
			
			if (rmNode->m_color == Color::BLACK)
			{
				removeFix(replaceNode, parent);//如果待删除结点是黑色就调整
			}

			delete rmNode;//释放内存
			rmNode = nullptr;
		}
		else
		{
			(value > node->m_value) ? remove(node->m_rightChild, value) : remove(node->m_leftChild, value);
		}
	}
}

红黑树删除被删除结点的情况

1.是根节点,这种情况不需要任何调整
2.是红色结点,删除之后不影响黑高,不调整
3.是黑色结点且不是根节点,删除之后就会影响黑高,就得调整

红黑树删除的调整

注意一个点,就是我们需要删除得节点是已经被删除了的,传进调整函数的是用来替换被删除节点的孩子节点(可能是null),所以有一点是不针对被删除结点的,替换删除结点的结点称之为replace
1.如果替换删除结点的结点replace是根结点(说明删除的结点时根结点)或者replace结点时红色的(是红色的或就将,此时不作调整),如果replace不为null,则将replace结点染成黑色的

2.不满足上面的情况,就要作细分判断
1)兄弟结点是黑色的,这种情况下,兄弟结点可能有孩子,也可能没有
(1)如果兄弟的没有孩子或者兄弟的两个孩子是黑色的,则又有两种情况:
a.父结点是红色,则将兄弟节点染成红色,父结点染成黑色,调整结束
b.父结点是黑色,则将兄弟节点染成红色,把父结点作为删除节点,向上调整

(2)剩下的孩子情况是(红,null),(null, 红),(黑,红),(红,黑),(红,红),那么,我们只需针对不同的情况去分析即可
a.如果兄弟节点是父结点是左孩子,且兄弟结点的左孩子存在且为红色,则将兄弟结点的颜色染成父结点的颜色,父结点染成黑色,兄弟结点的左孩子染成黑色,将父结点右旋(LL)

b.如果兄弟节点是父结点是右孩子,且兄弟结点的右孩子存在且为红色,则将兄弟结点的颜色染成父结点的颜色,父结点染成黑色,兄弟结点的右孩子染成黑色,将父结点左旋(RR)

c.如果兄弟节点是父结点是左孩子,且兄弟结点的右孩子存在且为红色,则将兄弟结点的右孩子的颜色染成父结点的颜色,父结点染成黑色,兄弟结点染成黑色,先将兄弟结点左旋,再将父结点右旋(LR)

d.如果兄弟节点是父结点是右孩子,且兄弟结点的左孩子存在且为红色,则将兄弟结点的左孩子的颜色染成父结点的颜色,父结点染成黑色,兄弟结点染成黑色,先将兄弟结点右旋,再将父结点左旋(RL)

2)兄弟结点时红色,这种情况下父结点时黑色的,兄弟结点一定右两个黑色的孩子,这种情况就把兄弟结点染成黑色,把父结点染成红色,如果兄弟结点是父结点的左孩子,则把父结点右旋,如果兄弟结点是父结点的右孩子,则将父结点左旋,这样处理之后,再获取兄弟结点,兄弟结点就是黑色结点了

红黑树删除的代码实现

void removeFix(RBTNodePtr replace, RBTNodePtr parent)
{
	/*如果替换删除结点的结点replace是根结点(说明删除的结点时根结点)或者
	replace结点时红色的(是红色的或就将,此时不作调整),*/
	if (replace == m_root || (replace != nullptr && replace->m_color == Color::RED))
	{
		/*如果replace不为null,则将replace结点染成黑色的*/
		if (replace)
			replace->m_color = Color::BLACK;
	}
	/*不满足上面的情况,就要作细分判断*/
	else
	{
		RBTNodePtr brother = (replace == parent->m_leftChild) ? parent->m_rightChild : parent->m_leftChild;
		if (!brother)
		{
			exit(1);
		}

		/*兄弟结点时红色,这种情况下父结点时黑色的,兄弟结点一定右两个黑色的孩子,
		这种情况就把兄弟结点染成黑色,把父结点染成红色,
		如果兄弟结点是父结点的左孩子,则把父结点右旋,
		如果兄弟结点是父结点的右孩子,则将父结点左旋,
		这样处理之后,再获取兄弟结点,兄弟结点就是黑色结点了*/
		if (brother->m_color == Color::RED)
		{
			brother->m_color = Color::BLACK;
			parent->m_color = Color::RED;
			(brother == parent->m_leftChild) ? rightRotate(parent) : leftRotate(parent);
		}

		/*兄弟结点是黑色的,这种情况下,兄弟结点可能有孩子,也可能没有*/
		brother = (replace == parent->m_leftChild) ? parent->m_rightChild : parent->m_leftChild;
		if (!brother)
		{
			exit(1);
		}
		/*如果兄弟的没有孩子或者兄弟的两个孩子是黑色的,则又有两种情况*/
		if ((brother->m_leftChild == nullptr || brother->m_leftChild->m_color == Color::BLACK) &&
			(brother->m_rightChild == nullptr || brother->m_rightChild->m_color == Color::BLACK))
		{
			/*父结点是红色,则将兄弟节点染成红色,父结点染成黑色,调整结束*/
			if (parent->m_color == Color::RED)
			{
				parent->m_color = Color::BLACK;
				brother->m_color = Color::RED;
			}
			/*父结点是黑色,则将兄弟节点染成红色,把父结点作为删除节点,向上调整*/
			else
			{
				brother->m_color = Color::RED;
				replace = parent;
				parent = replace->m_parent;
				removeFix(replace, parent);
			}
		}
		/*剩下的孩子情况是(红,null),(null, 红),(黑,红),(红,黑),(红,红),那么,我们只需针对不同的情况去分析即可*/
		else
		{
			/*如果兄弟节点是父结点是左孩子,且兄弟结点的左孩子存在且为红色,
			则将兄弟结点的颜色染成父结点的颜色,父结点染成黑色,兄弟结点的左孩子染成黑色,将父结点右旋(LL)*/
			if (brother == parent->m_leftChild && brother->m_leftChild && brother->m_leftChild->m_color == Color::RED)
			{
				brother->m_color = parent->m_color;
				parent->m_color = Color::BLACK;
				brother->m_leftChild->m_color = Color::BLACK;
				rightRotate(parent);
			}
			/*如果兄弟节点是父结点是右孩子,且兄弟结点的右孩子存在且为红色,
			则将兄弟结点的颜色染成父结点的颜色,父结点染成黑色,兄弟结点的右孩子染成黑色,将父结点左旋(RR)*/
			else if (brother == parent->m_rightChild && brother->m_rightChild && brother->m_rightChild->m_color == Color::RED)
			{
				brother->m_color = parent->m_color;
				parent->m_color = Color::BLACK;
				brother->m_rightChild->m_color = Color::BLACK;
				leftRotate(parent);
			}
			/*如果兄弟节点是父结点是左孩子,且兄弟结点的右孩子存在且为红色,
			则将兄弟结点的右孩子的颜色染成父结点的颜色,父结点染成黑色,兄弟结点染成黑色,先将兄弟结点左旋,再将父结点右旋(LR)*/
			else if (brother == parent->m_leftChild && brother->m_rightChild && brother->m_rightChild->m_color == Color::RED)
			{
				brother->m_rightChild->m_color = parent->m_color;
				parent->m_color = Color::BLACK;
				leftRotate(brother);
				rightRotate(parent);
			}
			/*如果兄弟节点是父结点是右孩子,且兄弟结点的左孩子存在且为红色,
			则将兄弟结点的左孩子的颜色染成父结点的颜色,父结点染成黑色,兄弟结点染成黑色,先将兄弟结点右旋,再将父结点左旋(RL)*/
			else if(brother == parent->m_rightChild && brother->m_leftChild && brother->m_leftChild->m_color == Color::RED)
			{
				brother->m_leftChild->m_color = parent->m_color;
				parent->m_color = Color::BLACK;
				rightRotate(brother);
				leftRotate(parent);
			}
		}
	}
}

红黑树删除总结

不要把replace结点当作真正删除节点看待,否则就会陷入万劫不复之中。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大唐不良猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值