红黑树结点的删除

红黑树结点删除这是我到目前为止见过最难的算法,但还是要搞懂啊,网上的博客千篇一律都是
列出五大性质,画个图,然后自己把自己说晕,就贴个代码糊弄人,评论区都来挑错,这让我很痛苦啊,看到知乎上有人说算法第四版作者就是发明这算法的,不巧,我看的就是算法第四版(他把结点删除弄成习题了),后来,才发现起点就是重点啊,作者在习题里给了代码,可我没分析懂,看到书上作者对删除的描述和习题答案,就发现了作者代码的意图,掌握了规律(网上的恐怕不是按他的真正思想来的吧,反正我是这么认为,就让我自欺欺人的以为发现了新大陆吧)
首先我们开始尝试删除key最小的结点(关于我的红黑树代码在上篇博客里,这里只会贴出部分分析的代码)
我们先来谈谈删除的思想
比如这样的树
这里写图片描述

其实我们知道红色节点其实和他的父节点是同一级别的,也就是这样:

这里写图片描述
删除最小key结点不就是28吗?那如果这棵树有很多结点怎么办??比上面情况复杂怎么办??很简单,我们先从树的根节点开始构造
红—黑—–红的线性序列,找到了我们要删除的,我们已经构造成功了,删除最小结点不就是红色的吗??(由红黑树性质可知,删除红结点不会破坏其平衡)其实破坏了也没关系,我们有函数收尾,下面我们来看看收尾的函数吧

/******************************************
函数名称:   balance
函数说明:   恢复红黑树的平衡
返回值:        Node*
*******************************************/
    Node* balance(Node* r)
    {
        if (isRed(r->right))
            r = rotateLeft(r);
        if (!isRed(r->left) && isRed(r->right))
            r = rotateLeft(r);
        if (isRed(r->left) && isRed(r->left->left))
            r = rotateRight(r);
        if (isRed(r->right) && isRed(r->left))
            flipColors(r);
        r->count = size(r->left) + size(r->right) + 1;
        return r;
    }

这函数就是对着红黑树的构造定义来的,你敢说用它这树还不平衡??
用它的时机也很简单,从根开始的结点到我们想要删除的节点都被我们转换成序列了,一棵树被我们弄成序列了,所以没找到的话就让balance()使其恢复平衡,在查找key时没找到就会在递归里面使其恢复平衡,然后向后继续查找。
那么,我们要考虑个问题,如何从根结点开始转换小红黑树为 红—黑—红,我们从根结点开始,根节点是黑的,左结点不确定,右结点必为黑.
如果左结点为红说明 我们要找的节点就是此节点(此时右节点为nullptr)
或者是此节点下还有一层那我们继续构造此时左结点已经为红了,原理就和第一次一样了,如果左结点为黑,似乎到了双黑的地步,可我们要注意,如果左结点的左节点也为红,为了红—黑—-红,我们会判断root左右结点是否都为黑结点,如果都为黑结点,我们会将变红,
可如果root->left->left = RED,就不行了,因为这样会导致
红—红–黑—红,不是作者说的4-结点,所以我们需要判断
于是便有下面代码

/******************************************
函数名称:  deleteMin
函数说明:  删除key最小的节点
返回值:    Node*
*******************************************/
    Node* deleteMin(Node* r)
    {
        if (r->left == nullptr)
            return nullptr;
        if (!isRed(r->left) && !isRed(r->left->left))
            r = moveRedLeft(r);
        r->left = deleteMin(r->left);
        return balance(r);
    }

删除完毕我们还要把root->color设为BLACK

void deleteMin()
    {
        if (root == nullptr)
            return;
        if (!isRed(root->left) && !isRed(root->right))
            root->color = RED;
        root = deleteMin(root);
        if (root != nullptr)
            root->color = BLACK;
    }
/******************************************
函数名称:  moveRedLeft
函数说明:  向左得到4-结点 即 红 黑 红
返回值:    Node*
*******************************************/
    Node* moveRedLeft(Node* r)
    {
        flipColors(r);
        if (r->right != nullptr && isRed(r->right->left))
        {
            r->right = rotateRight(r->right);
            r = rotateLeft(r);
        }
        return r;
    }

改变颜色形成 红—-黑——红—

/******************************************************
函数名称:  flipColors
函数说明:  若节点左右链接皆为红(黑)链接则将其改为黑(红)链接
返回值:    void
*******************************************************/
    void flipColors(Node* r)
    {
        r->color = static_cast<Color>(!r->color);
        r->left->color = static_cast<Color>(!r->left->color);
        r->right->color = static_cast<Color>(!r->right->color);
    }

上面其实无论多复杂·,你只要按照下面这个图的结构去敲就可以了

这里写图片描述
讨论的情况无非是给14下加个结点,或者是去掉18这个点

删除最大节点其原理也和上面差不多,无非就是要注意构造好的树右节点不可能为红,所以要注意 –红—-黑—红序列选取问题

/******************************************
函数名称:   deleteMax
函数说明:   删除key最大的节点
返回值:        Node*
*******************************************/
    Node* deleteMax(Node* r)
    {
        if (isRed(r->left))
            r = rotateRight(r);
        if (r->right == nullptr)
            return nullptr;
        if (!isRed(r->right) && !isRed(r->right->left))
            r = moveRedRight(r);
        r->right = deleteMax(r->right);
        return balance(r);
    }
void deleteMax()
    {
        if (root == nullptr)
            return;
        if (!isRed(root->left) && !isRed(root->right))
            root->color = RED;
        root = deleteMax(root);
        if (root != nullptr)
            root->color = BLACK;
    }
/******************************************
函数名称:  moveRedRight
函数说明:  向左得到4-结点 即 红 黑 红
返回值:    Node*
*******************************************/
    Node* moveRedRight(Node *r)
    {
        flipColors(r);
        if (isRed(r->left->left))
            r = rotateRight(r);
        return r;
    }

总结删除最大和最小节点,再利用删除二叉搜索树结点的方法,就可以得到删除任意节点的方法

/******************************************
函数名称:  erase
函数说明:  删除红黑树中与key相等的结点
返回值:    Node*
*******************************************/
    Node* erase(Node* r, const Key& k)
    {
        if (r->key > k)
        {
            if (!isRed(r->left) && !isRed(r->left->left))
                r = moveRedLeft(r);
            r->left = erase(r->left, k);
        }
        else
        {
            if (isRed(r->left))
                r = rotateRight(r);
            if (r->key == k && r->right == nullptr)
                return nullptr;
            if (!isRed(r->right) && !isRed(r->right->left))
                r = moveRedRight(r);
            if (r->key == k)
            {
                Node* t = min(r->right);
                r->key = t->key;
                r->value = t->value;
                r->right = deleteMin(r->right);
            }
            else
                r->right = erase(r->right,k);
        }
        return balance(r);
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值