2022-02-24 红黑树节点删除

一. 基于模拟2-3树而构造的红黑树的基本性质,重要!
  1. 红黑树是一种平衡二叉树

  2. 红黑树有两种节点,红节点red,黑节点black

  3. 连接到红节点的,都是左连接

  4. 没有任何一个节点连接两个红节点

  5. 黑节点 “ 完美平衡 ”,任意空节点到根节点之间,黑节点数量相同

二. 红黑树的逻辑基础:2-3树

什么是2-3树?

一个用二节点和三节点组成的平衡树。

也就是可以二叉,可以三叉。

我们来做个示例,我料想此文读者都了解二叉树,如不了解,请先看二叉搜索树的相关文献。

我们从a插入到g看看

a
a b
a b c

当出现4 节点,就需要升级,将中央节点 b 向上升位:

a
b
c

我们通过这种变换,使得树保持完美的平衡

a
b
c d

下面是重点,看看插入e的时候发生了什么

a
b
c d e

(c d e) 又形成了四节点,如何变换?

a
c
b d
e

d 进行了升位,加入到前序节点之中。使得树保持了完美的平衡。

a
c
b d
e f
a
c
b d
e f g

又出现了四节点,需要升位

a
c
b d f
e
g

再升位,依旧完美平衡。

a
b
c
d
e
f
g

我们看到,虽然是按照顺序进行输入,但是并没有产生像二叉树的退化,无论如何变换,依旧是一个完美平衡的树。

三. 红黑树模拟2-3树

构建一颗2-3树不太容易,但用修改的二叉树实现比较容易,这就是红黑树。

我们通过向左连接红色节点来模拟3节点:

红黑节点
3节点
a
b
a b

我们通过左右连接红节点模拟4节点

红黑节点
4节点
a
b
c
a b c
我们再通过从a插入到g看看

为了保持平衡,我们每次插入的都是红节点

根节点和空节点都是黑节点。

a

插入红节点 b

a
b

出现右支红节点,需要变换。

从 a 指向 b 变为由 b 指向 a,

同时 b 和 a 的颜色调换

a
b

插入红节点 c

a
b
c

当同时出现左右向的红色节点,就需要变色

将左右节点 a,c 变为黑节点,中央 b 节点变为红节点,但由于 b 是根节点,必须为黑节点。

a
b
c

我们通过这种变换,使得树保持完美的平衡

a
b
c
d

出现了右支红节点,需要进行变换

a
b
c
d

下面是重点,看看插入e的时候发生了什么

b
a
d
c
e

c = d = e 又形成了左右红节点,如何变换?

a
b
c
d
e

d 进行了变色,使得树保持了完美的平衡,但是出现了右支的红节点,需要进行转换。

a
b
c
d
e

继续插入 f

a
b
c
d
e
f

继续变

a
b
c
d
e
f

最后插入g

a
b
c
d
e
f
g

消除 e = f = g 的左右红节点

a
b
c
d
e
f
g

消除 b = d = f 的左右红节点

a
b
c
d
e
f
g

完美平衡。

总结一下

我们只用了3个条件,就能构造出黑节点完美平衡的红黑树:

  1. 每次插入红节点

  2. 插入右支红节点后,需变换为左支红节点

  3. 当一个节点连接两个红节点时,进行变色

我们漏了一点,就是红节点不能连接红节点,但这种情况很多,如逆序插入,需要进行简单变换:

a
b
c

红节点 b 连接红节点 a,需要转换为

a
b
c

再变,当b不是根节点,需要变红。

a
b
c

所以再加一条

  1. 当红节点连接红节点,需要转换为中黑左右红的形式,并进一步变色
代码解释:插入
	//左旋节点
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::rotateLeft(RBNode<K, V> *h)
    {
        RBNode<K, V> *x = h->right;
        h->right = x->left;
        x->left = h;
        x->color = h->color;
        h->color = RED;
        x->N = h->N;
        h->N = 1 + size(h->left) + size(h->right);
        return x;
    }
    
    //右旋节点
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::rotateRight(RBNode<K, V> *h)
    {
        RBNode<K, V> *x = h->left;
        h->left = x->right;
        x->right = h;
        x->color = h->color;
        h->color = RED;
        x->N = h->N;
        h->N = 1 + size(h->left) + size(h->right);
        return x;
    }

	//节点变色
    template <typename K, typename V>
    void ST::RBTree<K, V>::flipColors(RBNode<K, V> *h)
    {
        h->color = RED;
        h->left->color = BLACK;
        h->right->color = BLACK;
    }

    //put代码,插入节点
    template <typename K, typename V>
    void ST::RBTree<K, V>::put(const K &keys, const V &vals)
    {
        root = put(root, keys, vals); //调用递归put
        root->color = BLACK;          //重设根节点颜色
    }
    
    //递归put
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::put(RBNode<K, V> *h, const K &keys, const V &vals)
    {
        if (h == nullptr)   //未匹配键key时初始化节点,返回节点内存地址
        {
            return new RBNode<K, V>(keys, vals, 1, RED);
        }
        if (keys < h->key)  //插入的key小于节点key时,向左传递
        {
            h->left = put(h->left, keys, vals);
        }
        else if (keys > h->key)  //插入的key大于节点key时,向右传递
        {
            h->right = put(h->right, keys, vals);
        }
        else  //插入的key等于节点key,替换节点val
        {
            h->val = vals;
        }
        if (isRed(h->right) && !isRed(h->left))  //出现节点右支为红节点,左旋
        {
            h = rotateLeft(h);
        }
        if (isRed(h->left) && isRed(h->left->left)) //出现节点的左支及左支的左支为红节点,右旋
        {
            h = rotateRight(h);
        }
        if (isRed(h->left) && isRed(h->right))  //出现节点的左支和右支皆为红节点,改色
        {
            flipColors(h);
        }
        h->N = size(h->left) + size(h->right) + 1;  //调整节点的size数值
        return h;
    }

四. 红黑树的节点删除

删除节点比插入节点要复杂一些,但仍然是可以理解的。

咱们先从最简单的开始,删除最小节点

为了不影响平衡,删除的节点除根节点外,必须为红节点

没有左键,直接删除

原理很简单,没有左子节点的节点不能有右子节点,

因为右子节点的构造要求是黑节点,多出的黑节点导致红黑树黑节点不平衡,不能存在。

1
2
a

当左节点为红节点,直接向左传递

1
2
a
b
a

当左节点不为空,且不为红节点,变色,向左传递

1
2
a
b
c
a
b
c
3
a

以左连接红节点构造的二叉树,只有这三种形式

扩大到更多节点,也是如此,只需继续向左传递。

提高一点难度,删除最大节点

没有左节点时候,直接删除

1
2
a

左节点为红节点,要右旋,将右节点变红。

这也是插入右子节点的相反操作。

向右传递

1
2
a
b
a
b
3
b

当左节点不为空,且左节点为黑节点,需要还原为左右红节点,继续向右传递。

1
2
a
b
c
a
b
c
3
c

所以删除最大键也是三种形式:

和插入右节点是相反的操作。

接下来是最难的,但如果理解了删除最小键和最大键,就容易的多了。

删除一般键

删除普通键的策略是:

  1. 自根节点进行比较,如果小于本节点的key,按照删除最小键的方式向左传递。

  2. 如果大于本节点的key,按照删除最大键的方式向右传递。

  3. 如果等于本节点的key,如本节点的左节点为空,直接删除。

    key == a;

1
2
a
  1. 如果左节点不为空,且为红节点,则本节点的key等于左子树的最大key,本节点的val等于左子树的最大key的val。删除左子树的最大节点。

    key == b;
    若左子树最大节点key为a:

1
2
a
b
a
  1. 如果左节点不为空,且为黑节点,则变色,本节点的key等于左子树的最大key,本节点的val等于左子树的最大key的val。删除左子树的最大节点。

    key == b;
    若左子树最大节点key为a:

1
2
a
b
c
a
b
c
3
a
c
再平衡

我们虽然是按照插入的逆方法进行删除,但有一个问题,就是如何再平衡。

按照插入的配平方法,我们可以依次有序平衡红黑树。

但是删除的时候,路径中可能出现以前未能涉及到的情况,所以,配平的代码较插入时的配平更为复杂。

  1. 左右红节点,直接变色。
1
2
a
b
c
a
b
c
  1. 右节点为红及右节点的左节点红,右旋转右节点
1
2
d
e
i
j
d
e
j
i
  1. 右节点为红,左旋右节点
1
2
d
f
d
f
  1. 同1:左右红节点,直接变色。

**注意:**第4条不是废话,因为经过2,3的变换可能会变成4。如果去除1,则当左右节点为红,右节点的左节点为红时,会不停的旋转为一条单链,无法平衡。

代码解释:删除:

//再平衡
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::balance(RBNode<K, V> *h)
    {
        if (isRed(h->left) && isRed(h->right))
        {
            flipColors(h);
            h->N = size(h->left) + size(h->right) + 1;
            return h;
        }
        if (isRed(h->right) && isRed(h->right->left))
        {
            h->right = rotateRight(h->right);
        }
        if (isRed(h->right))
        {
            h = rotateLeft(h);
        }
        if (isRed(h->left) && isRed(h->right))
        {
            flipColors(h);
        }
        h->N = size(h->left) + size(h->right) + 1;
        return h;
    }

//向左传递
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::toLeftRed(RBNode<K, V> *h)
    {
        if (isRed(h->left))
        {
            return h;
        }
        else
        {
            reflipColors(h);
            return h;
        }
    }

//向右传递
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::toRightRed(RBNode<K, V> *h)
    {
        if (isRed(h->left))
        {
            h = rotateRight(h);
        }
        else
        {
            reflipColors(h);
        }
        return h;
    }

//删除最小key的节点
    template <typename K, typename V>
    void ST::RBTree<K, V>::delMin()
    {
        if (root)
        {
            root = delMin(root);
            if (root)
            {
                root->color = BLACK;
            }
        }
        else
        {
            return;
        }
    }

//删除最大key的节点
    template <typename K, typename V>
    void ST::RBTree<K, V>::delMax()
    {
        if (root)
        {
            root = delMax(root);
            if (root)
            {
                root->color = BLACK;
            }
        }
        else
        {
            return;
        }
    }

//递归删除最小节点,返回删除后的指针
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::delMin(RBNode<K, V> *h)
    {
        if (!h->left)
        {
            delete h;
            return nullptr;
        }
        h = toLeftRed(h);
        h->left = delMin(h->left);
        h = balance(h);
        return h;
    }

//递归删除最大节点,返回删除后的指针
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::delMax(RBNode<K, V> *h)
    {
        if (!h->left)
        {
            delete h;
            return nullptr;
        }
        h = toRightRed(h);
        h->right = delMax(h->right);
        h = balance(h);
        return h;
    }

//删除一般节点
    template <typename K, typename V>
    void ST::RBTree<K, V>::del(const K &keys)
    {
        if (root)
        {
            root = del(root, keys);
        }
        if (root)
        {
            root->color = BLACK;
        }
    }

//取得节点的value,返回pair,first表示是否含有key,second是返回的value
    template <typename K, typename V>
    std::pair<bool, V> ST::RBTree<K, V>::get(const K &keys)
    {
        RBNode<K, V> *x = root;
        while (x)
        {
            if (keys > x->key)
            {
                x = x->right;
            }
            else if (keys < x->key)
            {
                x = x->left;
            }
            else
            {
                return std::pair<bool, V>(true, x->val);
            }
        }
        return std::pair<bool, V>(false, V());
    }

//递归返回最大节点指针
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::max(RBNode<K, V> *x)
    {
        if (x->right == nullptr)
        {
            return x;
        }
        return max(x->right);
    }

//递归删除一般节点
    template <typename K, typename V>
    RBNode<K, V> *ST::RBTree<K, V>::del(RBNode<K, V> *h, const K &keys)
    {
        if (keys < h->key)
        {
            if (h->left)
            {
                h = toLeftRed(h);
                h->left = del(h->left, keys);
            }
            else
            {
                return h;
            }
        }
        else if (keys > h->key)
        {
            if (h->right)
            {
                h = toRightRed(h);
                h->right = del(h->right, keys);
            }
            else
            {
                return h;
            }
        }
        else
        {
            if (!h->left)
            {
                delete h;
                return nullptr;
            }
            else
            {
                h->key = max(h->left)->key;
                h->val = get(h->left, h->key).second;
                h = toLeftRed(h);
                h->left = delMax(h->left);
            }
        }
        return balance(h);
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不停感叹的老林_<C 语言编程核心突破>

不打赏的人, 看完也学不会.

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

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

打赏作者

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

抵扣说明:

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

余额充值