红黑树[删除]

67 阅读4分钟

删除一个节点要保证根节点是黑色,每个路径黑色节点的数量要和删除之前保持一致

根据有孩子分为几种情况:

有两个孩子、有一个孩子(左孩子或有孩子)、没有孩子。

那么将有一个孩子的转换成有一个孩子或者没有孩子情况

找到前驱节点,前驱节点的覆盖到要删除的节点上,然后删除前驱节点,这样就转换成后两种情况.

根据删除节点的颜色分为几种情况
  1. 删除的是红色节点,删除之后不违反红黑树规则,不用做任何调整
  2. 删除的是黑色节点,分为两种情况:

① 如果补上来的孩子是红色节点,则直接把这个孩子涂成黑色,调整完成 ② 如果不上来的孩子是黑色节点,没有办法在这个路径补出一个黑色节点,需要从兄弟那里借一个黑色节点,这又分为4种情况:

下列都以删除节点B为例
情况1:

节点B的兄弟节点是黑色,兄弟节点的右子节点有一个红色(左、右子节点都为红色也归结为情况1)

image.png


情况2:

节点B的兄弟节点是黑色,兄弟节点只有左子节点红色

image.png


情况3:

节点B的兄弟节点是黑色,但是兄弟节点没有红色子节点,那么就让两边路径各自少一个黑色节点,然后父节点向上回溯,碰到红色节点就涂成黑色(调整完成),如果碰到了黑色节点,那么就按照其他三种情况处理。
image.png


情况4:

节点B的兄弟节点是红色

image.png


删除代码

template <typename T>
class RBTree
{
    enum Color { BLACK = 1, RED };
    struct Node;
public:
    RBTree() {}
    ~RBTree() { /* 三种遍历方式进行析构 */ }
    // 左旋
    void leftRotate(Node* node)
    {
        if (node == nullptr) return;

        Node* child = node->right;
        setParent(child, parent(node));
        if (parent(node) == nullptr)
            m_root = child;
        else
        {
            if (parent(node)->left == node)
                parent(node)->left = child;
            else
                parent(node)->right = child;
        }
        node->right = child->left;
        if (node->right) setParent(node->right, node);
            child->left = node;
        setParent(node, child);
    }
    // 右旋
    void rightRotate(Node* node)
    {
        if (node == nullptr) return;

        Node* child = node->left;
        setParent(child, parent(node));
        if (parent(node) == nullptr)
            m_root = child;	// 当前节点是根节点
        else
        {
            if (parent(node)->left == node)
                parent(node)->left = child;
            else
                parent(node)->right = child;
        }
        node->left = child->right;
        if (node->left)
            setParent(node->left, node);
        child->right = node;
        setParent(node, child);
    }
    
    // 删除
    void remove(const T& data)
    {
        if (m_root == nullptr) return;

        Node* node = m_root;
        while (node)
        {
            if (node->data > data)
                node = node->left;	// 去左子树查找
            else if (node->data < data)
                node = node->right;	// 去右子树查找
            else break;	// 找到
        }
        if (!node) return;	// 没有找到则退出

        // 有两个子节点的情况,转换为只有一个子节点或没子节点的情况
        if (node->left && node->right)
        {
            // 找到前驱节点
            Node* prev = node->left;
            while (prev->right)
            prev = prev->right;

            node->data = prev->data;	// 前驱节点的值覆盖当前节点, 然后删除前驱节点
            node = prev;
        }

        Node* child = node->left;
        if (child == nullptr)
            child = node->right;
        if (child != nullptr)    // 只有一个子节点的情况
        {
            setParent(child, parent(node));	// 设置子节点的父节点
            if (parent(node) == nullptr)
                m_root = child;	// 子节点是新的根节点
            else
            {
                // 将父节点和子节点关联起来
                if (parent(node)->left == node)
                    parent(node)->left = child;	
                else
                    parent(node)->right = child;
            }
            // 删除当前节点
            Color c = color(node);	// 记录删除节点的颜色
            delete node;

            if (c == BLACK)	// 删除的是黑色节点需要调整
                fixAfterRemove(child);
        }
        else    // 两个子节点都为空
        {
            if (parent(node) == nullptr)
            {
                // 当前删除的节点就是根节点,而且没有子节点作为新的根节点
                delete node;
                m_root = nullptr;
                return;
            }
            else
            {
                if (color(node) == BLACK)	// 删除的是黑色节点需要调整
                    fixAfterRemove(node);

                if (parent(node)->left == node)
                    parent(node)->left = nullptr;
                else
                    parent(node)->right = nullptr;
                delete node;
                return;
            }
        }
    }
    
    void fixAfterRemove(Node* node)	// 删除节点调整
    {
        while (m_root != node && color(node) == BLACK)
        {
            if (parent(node)->left == node)
            {
                // 删除的黑色节点在左子树
                Node* brother = parent(node)->right;
                if (color(brother) == RED)	// 情况4
                {
                    setColor(brother, BLACK);
                    setColor(parent(node), RED);
                    leftRotate(parent(node));
                    brother = parent(node)->right;
                }

                // 两个节点都为黑色
                if (color(brother->left) == BLACK 
                        && color(brother->right) == BLACK)	// 情况3
                {
                    setColor(brother, RED);
                    node = parent(node);
                }
                else
                {
                    // 进入这里表示有一个子节点肯定为红色,要不全是红色
                    if (color(brother->right) != RED)	// 情况2
                    {
                        setColor(brother, RED);
                        setColor(brother->left, BLACK);
                        rightRotate(brother);
                        brother = parent(node)->right;
                    }

                    // 情况1
                    setColor(brother, color(parent(node)));
                    setColor(parent(node), BLACK);
                    setColor(brother->right, BLACK);
                    leftRotate(parent(node));
                    break;
                }
            }
            else
            {
                // 删除的黑色节点在右子树
                Node* brother = parent(node)->left;
                if (color(brother) == RED)	// 情况4
                {
                    setColor(brother, BLACK);
                    setColor(parent(node), RED);
                    rightRotate(parent(node));
                    brother = parent(node)->left;
                }

                // 两个节点都为黑色
                if (color(brother->left) == BLACK
                    && color(brother->right) == BLACK)	// 情况3
                {
                    setColor(brother, RED);
                    node = parent(node);
                }
                else
                {
                    // 进入这里表示有一个子节点肯定为红色,要不全是红色
                    if (color(brother->left) != RED)	// 情况2
                    {
                        setColor(brother, RED);
                        setColor(brother->right, BLACK);
                        leftRotate(brother);
                        brother = parent(node)->left;
                    }

                    // 情况1
                    setColor(brother, color(parent(node)));
                    setColor(parent(node), BLACK);
                    setColor(brother->left , BLACK);
                    rightRotate(parent(node));
                    break;
                }
            }
        }
        // 当前节点是红色直接设置成黑色
        setColor(node, BLACK);	
    }
};   

测试代码

#include <iostream>

using namespace std;

int main()
{
    RBTree<int> tree;
	
    for (int i = 1; i <= 10; i++)
        tree.insert(i);

    tree.remove(9);
    tree.remove(10);
    tree.remove(5);
    tree.remove(4);

    return 0;
}

测试结果

image.png