红黑树[插入]

68 阅读3分钟

特点:

不是一颗平衡树,左右子树的高度差,长的不超过短的2倍。

  1. 树的每一个节点都有颜色
  2. NULL是黑色
  3. root是黑色
  4. 不能出现连续的红色节点
  5. 从root根节点到每一个叶子节点的路径上,黑色节点的数量是相同的

image.png 从上图中可以看出,对于只需要增、查操作的话,AVL树要更好一些。但是对于大数据量,并且需要删除操作,那么红黑树整体下来性能要更好,因为旋转的次数要更少些。所以,为什么stl会选择红黑树作为set、map容器的底层数据结构。

插入操作

  1. 如果是空树,则直接插入作为根节点, 节点颜色: [黑色]
  2. 如果非空树,插入节点为叶子节点, 节点颜色: [红色]. 此时要检查父节点的颜色,如果父节点颜色是黑色,插入完成。否则出现连续的红色节点,开始做插入调整。

以下只是一侧的情况,左侧和右侧加一起一共有六种情况

情况1:

image.png

情况2:

image.png

情况3:

将左子树都变为一侧然后以情况2处理

image.png

插入代码:

代码中的每一个情况都是以上图而编写,熟练写几遍,脑子里能记住上面几个图,代码就很容易写出来了。

template <typename T>
class RBTree
{
    enum Color { BLACK = 1, RED };    // 定义颜色
    struct Node;    // 声明结构体类型
public:
    RBTree() {}
    ~RBTree() { /* 三种遍历方式进行析构 */ }
    void insert(const T& data)    // 插入函数
    {
        if (m_root == nullptr)
        {
            // 根节点为空,插入元素作为根节点
            m_root = new Node(data);
            return;
        }

        Node* curr = m_root;
        Node* p = nullptr;
        while (curr)
        {
            p = curr;
            if (data > curr->data)    // 插入的元素比当前元素小
                curr = curr->right;  // 往右子树遍历
            else if (data < curr->data)    // 插入的元素比当前元素大
                curr = curr->left;  // 往左子树遍历
            else return;	// 如果存在该元素则不插入退出函数
        }
        Node* node = new Node(data, RED, p);   // 生成一个节点,颜色为红色

        if (data > p->data)    // 向右子树插入节点
            parent(node)->right = node;
        else parent(node)->left = node;    // 向左子树插入节点

        if (color(parent(node)) == RED)
            fixAfterInsert(node);    // 当前节点和父节点都为红色则需要调整
     }
private:
    // 以下为几个便捷的函数
    inline void setParent(Node* node, Node* parent) { node->parent = parent; }
    inline Node* parent(Node* node) { return node->parent; }
    inline Color color(Node* node) 
    {  return node == nullptr ? BLACK : node->color; }
    inline void setColor(Node* node, Color color) 
    { if (!node) return;  node->color = color; }
private:
    // 插入元素需要调整
    void fixAfterInsert(Node* node)
    {
        while (color(parent(node)) == RED)
        {
            if (parent(parent(node))->left == parent(node))
            {
                // 插入的节点在左子树
                Node* uncle = parent(parent(node))->right;
                if (color(uncle) == BLACK)
                {
                    // 情况3
                    if (parent(node)->right == node)
                    {
                        node = parent(node);
                        leftRotate(node);
                    }

                    // 情况2
                    setColor(parent(node), BLACK);
                    setColor(parent(parent(node)), RED);
                    rightRotate(parent(parent(node)));
                    break;	// 调整完成
                }

                // 情况1
                setColor(parent(node), BLACK);
                setColor(parent(parent(node)), RED);
                setColor(uncle, BLACK);
                node = parent(parent(node));	// 以爷爷节点继续向上回溯
            }
            else
            {
                // 插入的节点在右子树
                Node* uncle = parent(parent(node))->left;
                if (color(uncle) == BLACK)
                {
                    // 情况3
                    if (parent(node)->left == node)
                    {
                        node = parent(node);
                        rightRotate(node);
                    }

                    // 情况2
                    setColor(parent(node), BLACK);
                    setColor(parent(parent(node)), RED);
                    leftRotate(parent(parent(node)));
                    break;	// 调整完成
                }
                // 情况1
                setColor(parent(node), BLACK);
                setColor(parent(parent(node)), RED);
                setColor(uncle, BLACK);
                node = parent(parent(node));	// 以爷爷节点继续向上回溯
            }
        }
        setColor(m_root, BLACK);
    }
};

测试代码

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

插入后的红黑树如下图所示

image.png