删除操作:
二叉树的删除操作:

若要删除值’5’节点,我们一般不是直接摘除’5’节点,而是将4或者6的赋值给’5’节点,然后再删除’4’节点或者‘6’节点,也就是将根节点的删除改为删除叶子节点和有一个子节点的节点。直接摘除5会增加许多不必要的指针操作。
复习一下,常规删除操作:
- 叶子节点直接删除
- 删除节点有一个子节点,直接使用子节点替代
- 如果被删除节点有两个子节点,使用前驱或后继节点来替代被删除节点。
被删除的前驱节点和后继节点只有两种情况:
- 被删除节点是叶子节点。
- 被删除节点只有一个孩子。
由于被删除节点只有以上两种情况,因此红黑树上的删除节点一定是2-3-4树的叶子节点。故红黑树要删除的节点是叶子节点或叶子节点上面一层,对应2-3-4树就是叶子节点。
在2-3-4树的叶子节点中有2节点3节点和4节点,删除叶子节点时,有两种合法情况,第一种是在3节点中删除一个元素是合法的,第二种是在4节点中删除一或者两个元素是合法的。
基于以上删除操作可知,在算法中找到前驱节点和后继节点至关重要。而红黑树在删除的过程中涉及到许多的调整,具体实现看如下代码:
/*删除操作:
*1、删除叶子节点直接删除
*2、被删除的节点有一个子节点,删除它之后使用子节点替代它
*3、被删除的节点有两个子节点,查找前继或后续节点来替代被删除节点
*/
template <class K,class V>
void RBtree<K, V>::deleteNode(RBtreeNode<K, V>* p)
{
// 有两个子节点的情况,先进行节点替换
if ( p->left != nullptr && p->right != nullptr)
{
// 使用后续节点
RBtreeNode<K, V>* p_successor = successor(p);
// 使用前继节点
// RBtreeNode<K, V>* p_predecessor = predecessor(p);
// 使用前继或者后续的值来替代原节点的值
p->key = p_successor->key;
p->value = p_successor->value;
p = p_successor; //指向要删除节点
}
// 节点替换后,情况3就变成了1和2两种情况,后继节点为便为2-3-4树的叶子节点
// 删除节点看看有没有左右子树
RBtreeNode<K, V>* replacement = p->left != nullptr ? p->left : p->right;
if (replacement != nullptr) // 删除节点有子树
{
replacement->parent = p->parent; //先把父亲绑上
if (p->parent == nullptr) // 说明p是根节点,用replacement来替代根节点
{
this->root = replacement;
}
else if (p == p->parent->left)
{
p->parent->left = replacement;
}
else if (p == p->parent->right)
{
p->parent->right = replacement;
}
// 若被删除颜色为黑色,因为破坏了平衡
// 所以此时需要调整
if (p->color == BLACK)
{
// 此时改颜色就好了
fixAfterdelete(replacement);
}
p->parent = nullptr;
p->left = nullptr;
p->right = nullptr;
}
else if (replacement == nullptr)//若没有子节点则说明替代节点为叶子节点
{
// 因为要保证黑色平衡,所以若删除节点是黑色要先调整
// 调整过程其实为像兄弟姐妹来借子节点,具体实现仍然是依靠左右旋的算法
if (p->color == BLACK)
{
fixAfterdelete(p);
}
//再删除
//这里多添加根节点的判断是因为经过fixAfterRemove()之后
//p可能变成根节点
if (p->parent != nullptr)
{
if (p == p->parent->left)
{
p->parent->left = nullptr;
}
else if (p == p->parent->right)
{
p->parent->right = nullptr;
}
p->parent = nullptr; // 双向清空
}
}
else if (p->parent == nullptr) // 删除节点就是根节点
{
this->root = nullptr;
}
}
template<class K,class V>
void RBtree<K, V>::fixAfterdelete(RBtreeNode<K, V>* p)
{
while (p != root && colorof(p)==BLACK) // p为根节点的时候直接退出循环
{
//先判断p是父节点的左子还是右子
if (p->parent->left == p) // 若是左子树
{
RBtreeNode<K, V>* rnode = p->parent->right; // 取兄弟节点
if (rnode->color == RED)// 若兄弟为红色,说明该节点是假兄弟节点(其实一定会有兄弟的,所以不需要判断兄弟的有无(因为2-3-4树的性质))
{
// 当兄弟节点是红色的时候,其恰好为假兄弟节点
// 那么就需要做相应的处理,用以查找真正的兄弟节点
// 此时在2-3-4树中,其父节点为一黑一红的3节点,红色便为p的假兄弟节点
// 因为是三节点树故必须有三个子节点,当我自身父节点的右子节点为假兄弟节点时
// 说明其他两个子节点连接在红色的下面,此时只需要左旋即可
// x(黑)
// / \
// p(黑) 假兄弟节点(红)
// / \
// a(黑) b(黑) 其中a,b为真兄弟节点
rnode->parent->color = RED;
rnode->color = BLACK;
leftRotate(rnode->parent); //左旋
// 找到真正的兄弟节点
rnode = p->parent->right;
}
//找兄弟节点借,若兄弟节点没的借
if (rnode->left->color==BLACK && rnode->right->color== BLACK)
{
//没的借,先将兄弟节点本身去红保证子树平衡
//但对于上面的又不平衡因此需要不断的递归
//直到遇到一组树中根节点为红节点,
//那么便停止递归(在while外面将其染成黑色即可)
rnode->color = RED;
p = p->parent;
}
else //若兄弟节点有的借
{
if (rnode->right->color==BLACK)//若右节点空。则左节点一定有东西,此时需要旋转处理
{
//旋转前先变色
rnode->left->color = BLACK;
rnode->color = RED;
//旋转
rightRotate(rnode);
}
// 三四节点都要经历这种变色和旋转
rnode->color = rnode->parent->color;
rnode->parent->color = BLACK;
rnode->right->color = BLACK;
// 旋转
leftRotate(rnode->parent);
// 有的借之后便可以直接结束循环
p = root; //将指针赋给根节点结束循环
}
}else//若是右子节点
{
RBtreeNode<K, V>* lnode = p->parent->left;
if (lnode->color == RED)// 若兄弟为红色,其实一定会有兄弟的,所以不需要判断兄弟的有无(因为2-3-4树的性质)
{
lnode->parent->color = RED;
lnode->color = BLACK;
rightRotate(lnode->parent); //左旋
// 找到真正的兄弟节点
lnode = p->parent->left;
}
//找兄弟节点借,若兄弟节点没的借
if (lnode->right->color == BLACK && lnode->left->color == BLACK)
{
//情况复杂,等下再写
lnode->color = RED;
p = p->parent;
}
else //若兄弟节点有的借
{
if (lnode->left->color == BLACK)//若右节点空。则左节点一定有东西,此时需要旋转处理
{
//旋转前先变色
lnode->right->color = BLACK;
lnode->color = RED;
//旋转
leftRotate(lnode);
}
// 三四节点都要经历这种变色和旋转
lnode->color = lnode->parent->color;
lnode->parent->color = BLACK;
lnode->left->color = BLACK;
// 旋转
rightRotate(lnode->parent);
// 有的借之后便可以直接结束循环
p = root; //将指针赋给根节点结束循环
}
}
}
// 若为红色的情况则直接变黑
p->color = BLACK;
}
版权声明:本文为qq_42138448原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。