第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结点当作真正删除节点看待,否则就会陷入万劫不复之中。