map/set/multimap/multiset 的底层逻辑与实现

目录

底层原理

红黑树

红黑树结构

红黑树插入

[情况一: cur为红,p为红,g为黑,u存在且为红。](#情况一: cur为红,p为红,g为黑,u存在且为红。)

[情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑](#情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑)

[情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑](#情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑)

红黑树的验证

红黑树整体结构与头结点

左旋

右旋

迭代器自增

迭代器自减

Find

Clear

代码(总)

红黑树与AVL树的比较

红黑树的应用

红黑树模拟实现STL中的map与set

红黑树的迭代器

[红黑树模拟实现 STL 中的 map 与 set](#红黑树模拟实现 STL 中的 map 与 set)

红黑树的迭代器

迭代器定义

[operator++ 的实现思路](#operator++ 的实现思路)

_Increasement

[operator-- 的实现思路](#operator-- 的实现思路)

_Decreasement

改造红黑树

改造后的红黑树模板

Begin

End

Insert

Find

[map 的模拟实现](#map 的模拟实现)

MapKeyOfValue

[map 的类定义](#map 的类定义)

begin

end

insert

find

operator\[\]

其他辅助接口

[set 的模拟实现](#set 的模拟实现)

SetKeyOfValue

[set 的类定义](#set 的类定义)

begin

end

insert

find

其他辅助接口

完整代码


底层原理

map/set等关联式容器的底层通常用平衡二叉搜索树(如红黑树)实现,而非普通的二叉搜索树。这是因为若使用普通二叉搜索树,当插入有序数据时,树会退化成近似单链的结构,使操作时间复杂度上升为O(N)。平衡树通过特定的旋转规则维持树的平衡,从而保证了操作的高效性(O(log N))。

红黑树

红黑树,是一种二叉搜索树 ,但在每个结点上增加一个存储位表示结点的颜色 ,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍,因而是接近平衡的。

性质:

  1. 每个结点不是红色就是黑色:利用这两种颜色去限制树形结构。

  2. 根节点是黑色的

  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的

  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点

  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点 个数的两倍?

答:最短路径,尽量全是黑节点;最长路径,黑节点之间夹红节点;又因为所有路径要求黑节点数必须一致,所以出现了这种情况:最短路径全为黑节点,最长路径两黑一红,所以最长路径就是最短路径的两倍。

结点的定义;

cpp 复制代码
enum Color
{
    RED,
    BLACK
};

template<class T>
struct RBTreeNode
{
    RBTreeNode(const T& data = T(), Color color = RED)
        : _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _color(color)
    {}

    RBTreeNode<T>* _left;    // 左孩子
    RBTreeNode<T>* _right;   // 右孩子
    RBTreeNode<T>* _parent;  // 父节点
    T _data;                 // 节点中存储的数据
    Color _color;            // 节点颜色
};

**在节点的定义中,为什么要将节点的默认颜色给成红色?:**插红只可能破坏"双红",插黑可能破坏"整条路径黑高"

如果插入黑色,会更容易破坏黑高 假设你插入一个黑节点,那么它所在路径上的黑节点数立刻 +1。这就很容易破坏性质 :所有路径黑节点数相同。

一旦黑高不一致,调整起来很麻烦,影响会一路向上。

如果插入红色,通常不影响黑高

因为红节点不计入黑节点数量,所以插入红节点后:黑高通常没变;唯一可能破坏的是性质 3:父子不能同红。而"双红冲突"只需要局部修正,成本更低。

红黑树结构

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了 与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft 域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:

  • head->_parent 指向根

  • head->_left 指向最小节点

  • head->_right 指向最大节点

红黑树插入

红黑树插入分两步

第一步仍然是普通二叉搜索树插入

第二步是插入后的颜色修正

这里最重要的是:

新节点默认是红色

如果父节点是黑色,不会破坏红黑树性质,直接结束

如果父节点也是红色,就违反了"不能有连续红色节点"这一条性质,这时必须调整

调整时分三大类

  • 叔叔存在且为红,变色并继续向上处理
  • 叔叔不存在或为黑,并且当前节点和父节点、祖父节点在一条直线上,单旋加变色
  • 叔叔不存在或为黑,并且当前节点和父节点、祖父节点形成折线,先旋成直线,再按上一种处理
cpp 复制代码
public:
    std::pair<Iterator, bool> Insert(const ValueType& data)
    {
        KeyOfValue keyOfValue;
        Node*& root = _GetRoot();

        // 如果树为空,直接插入根节点
        // 根节点必须为黑色
        if (root == nullptr)
        {
            root = new Node(data, BLACK);
            root->_parent = _head;
            _head->_left = root;
            _head->_right = root;
            ++_size;
            return std::make_pair(Iterator(root), true);
        }

        // 按照二叉搜索树规则查找插入位置
        Node* parent = root;
        Node* cur = root;
        K key = keyOfValue(data);

        while (cur)
        {
            parent = cur;

            if (key < keyOfValue(cur->_data))
            {
                cur = cur->_left;
            }
            else if (key > keyOfValue(cur->_data))
            {
                cur = cur->_right;
            }
            else
            {
                return std::make_pair(Iterator(cur), false);
            }
        }

        // 创建新节点
        // 红黑树中新插入节点默认给红色
        Node* newNode = new Node(data, RED);
        newNode->_parent = parent;

        if (key < keyOfValue(parent->_data))
        {
            parent->_left = newNode;
        }
        else
        {
            parent->_right = newNode;
        }

        // 更新最小节点和最大节点
        if (_head->_left == _head || key < keyOfValue(_head->_left->_data))
        {
            _head->_left = newNode;
        }

        if (_head->_right == _head || key > keyOfValue(_head->_right->_data))
        {
            _head->_right = newNode;
        }

        ++_size;

        // 从新节点开始向上检查是否出现连续红色节点
        cur = newNode;
        parent = cur->_parent;

        while (parent != _head && parent->_color == RED)
        {
            // 既然父节点是红色,那么祖父节点一定存在
            Node* grandfather = parent->_parent;

            // 先处理父节点在祖父左边的情况
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;

                // 叔叔存在且为红色
                // 这种情况只需要变色,然后把祖父当成新的当前节点继续向上处理
                if (uncle && uncle->_color == RED)
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandfather->_color = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    // 如果当前节点在父节点右边
                    // 说明形成了折线,先对父节点做左旋,把它转成直线
                    if (cur == parent->_right)
                    {
                        _RotateL(parent);
                        std::swap(cur, parent);
                    }

                    // 走到这里,一定变成了直线情况
                    // 父节点染黑,祖父节点染红,然后对祖父做右旋
                    parent->_color = BLACK;
                    grandfather->_color = RED;
                    _RotateR(grandfather);
                    break;
                }
            }
            else
            {
                // 父节点在祖父右边时,处理逻辑完全对称
                Node* uncle = grandfather->_left;

                if (uncle && uncle->_color == RED)
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandfather->_color = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    if (cur == parent->_left)
                    {
                        _RotateR(parent);
                        std::swap(cur, parent);
                    }

                    parent->_color = BLACK;
                    grandfather->_color = RED;
                    _RotateL(grandfather);
                    break;
                }
            }
        }

        // 无论中间怎么调整,根节点最后都必须重新染成黑色
        root->_color = BLACK;
        root->_parent = _head;

        return std::make_pair(Iterator(newNode), true);
    }

检测新节点插入后,红黑树的性质是否造到破坏,若满足直接退出,否则对红黑树进行旋转着色处理

新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何 性质,则不需要调整;

但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连 在一起的红色节点,此时需要对红黑树分情况来讨论:

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

情况一: cur为红,p为红,g为黑,u存在且为红。

cur和p均为红,违反了性质三,此处能否将p直接改为黑?

解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,

p为g的右孩子,cur为p的右孩子,则进行左单旋转

p、g变色--p变黑,g变红

情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,

p为g的右孩子,cur为p的左孩子,则针对p做右单旋转

则转换成了情况2

红黑树的验证

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

  2. 检测其是否满足红黑树的性质

这里我把他整理成了公开接口加私有递归函数两层

IsValidRBTree

cpp 复制代码
    bool IsValidRBTree() const
    {
        Node* root = _head->_parent;

        // 空树也是红黑树
        if (root == nullptr)
        {
            return true;
        }

        // 根节点必须为黑色
        if (root->_color != BLACK)
        {
            return false;
        }

        // 先取最左路径上的黑色节点个数作为基准
        size_t blackCount = 0;
        Node* cur = root;
        while (cur)
        {
            if (cur->_color == BLACK)
            {
                ++blackCount;
            }
            cur = cur->_left;
        }

        size_t pathBlack = 0;
        return _IsValidRBTree(root, pathBlack, blackCount);
    }

_IsValidRBTree

这个递归函数主要检查两件事

是否存在连续红色节点

每条路径上的黑色节点个数是否相同

cpp 复制代码
private:
    bool _IsValidRBTree(Node* root, size_t pathBlack, const size_t blackCount) const
    {
        // 走到空节点时,检查当前路径上的黑色节点数是否和基准一致
        if (root == nullptr)
        {
            return pathBlack == blackCount;
        }

        // 当前节点如果是黑色,就把黑色计数加 1
        if (root->_color == BLACK)
        {
            ++pathBlack;
        }

        // 如果当前节点和父节点都为红色,就违反了性质
        if (root->_parent != _head &&
            root->_parent->_color == RED &&
            root->_color == RED)
        {
            return false;
        }

        return _IsValidRBTree(root->_left, pathBlack, blackCount) &&
               _IsValidRBTree(root->_right, pathBlack, blackCount);
    }

红黑树整体结构与头结点

红黑树这里和普通二叉搜索树最大的不同,不只是颜色,还有一个专门的头结点 _head

这个头结点不是有效数据节点,它主要有三个作用:

  • _head->_parent 指向根节点
  • _head->_left 指向整棵树中最小的节点
  • _head->_right 指向整棵树中最大的节点

这样设计以后有几个明显好处:

  • 获取根节点更方便
  • begin() 可以直接返回最小节点
  • end() 可以直接放在 _head
  • 迭代器做 --end() 时,可以直接回到最大节点

所以头结点其实是为了后面实现迭代器和模拟 map/set 做准备的

cpp 复制代码
template<class K, class ValueType, class KeyOfValue>
class RBTree
{
    using Node = RBTreeNode<ValueType>;

public:
    using Iterator = RBTreeIterator<ValueType, ValueType&, ValueType*>;

    RBTree()
        : _size(0)
    {
        // 创建头结点
        _head = new Node;

        // 头结点颜色给红色,主要是为了和普通根节点区分
        _head->_color = RED;

        // 头结点初始化时还没有根节点
        _head->_parent = nullptr;

        // 空树时,最小节点和最大节点都先指向头结点自己
        _head->_left = _head;
        _head->_right = _head;
    }

private:
    // 头结点的 parent 域就是根节点
    Node*& _GetRoot()
    {
        return _head->_parent;
    }

private:
    Node* _head;
    size_t _size;
};

左旋

左旋处理的是"右边上升"的结构变化

它本质上做了这几件事:

  • parent 的右孩子 subR 上升
  • parent 下沉到 subR 的左边
  • subR 原来的左子树接到 parent 的右边
  • 最后把新子树根重新接回原来的祖先节点

左旋和右旋是红黑树插入调整的基础操作,插入修复时不管是直线型还是折线型,最后都要落到这两个旋转上

cpp 复制代码
void _RotateL(Node* parent)
{
    // 1. 先记录 parent 的右孩子
    Node* subR = parent->_right;

    // 2. 再记录 subR 的左子树
    //    旋转后,这棵子树要接到 parent 的右边
    Node* subRL = subR->_left;

    // 3. 保存 parent 原来的父节点
    Node* ppNode = parent->_parent;

    // 4. 让 parent 接住 subR 的左子树
    parent->_right = subRL;
    if (subRL)
    {
        subRL->_parent = parent;
    }

    // 5. 让 subR 上升,parent 下沉到 subR 左边
    subR->_left = parent;
    subR->_parent = ppNode;
    parent->_parent = subR;

    // 6. 把旋转后的新子树根接回原来的位置
    if (ppNode == _head)
    {
        _head->_parent = subR;
    }
    else if (ppNode->_left == parent)
    {
        ppNode->_left = subR;
    }
    else
    {
        ppNode->_right = subR;
    }
}

右旋

右旋和左旋完全对称

它本质上做的是:

  • parent 的左孩子 subL 上升
  • parent 下沉到 subL 的右边
  • subL 原来的右子树接到 parent 的左边
  • 最后把新子树根重新接回原来的祖先节点
cpp 复制代码
void _RotateR(Node* parent)
{
    // 1. 先记录 parent 的左孩子
    Node* subL = parent->_left;

    // 2. 再记录 subL 的右子树
    //    旋转后,这棵子树要接到 parent 的左边
    Node* subLR = subL->_right;

    // 3. 保存 parent 原来的父节点
    Node* ppNode = parent->_parent;

    // 4. 让 parent 接住 subL 的右子树
    parent->_left = subLR;
    if (subLR)
    {
        subLR->_parent = parent;
    }

    // 5. 让 subL 上升,parent 下沉到 subL 右边
    subL->_right = parent;
    subL->_parent = ppNode;
    parent->_parent = subL;

    // 6. 把旋转后的新子树根接回原来的位置
    if (ppNode == _head)
    {
        _head->_parent = subL;
    }
    else if (ppNode->_left == parent)
    {
        ppNode->_left = subL;
    }
    else
    {
        ppNode->_right = subL;
    }
}

迭代器自增

迭代器 ++ 的本质,是找当前节点的中序后继

分两种情况:

  • 当前节点右子树存在

    后继就是右子树中最左侧的节点

  • 当前节点右子树不存在

    就沿着父节点一路往上找,直到找到一个祖先,使得当前节点位于这个祖先的左边,那么这个祖先就是后继

cpp 复制代码
void _Increasement()
{
    // 如果右子树存在
    // 后继一定在右子树中,并且是右子树里最左侧的节点
    if (_node->_right)
    {
        _node = _node->_right;
        while (_node->_left)
        {
            _node = _node->_left;
        }
    }
    else
    {
        // 如果右子树不存在
        // 就沿着父节点向上找,直到当前节点不是父节点的右孩子
        Node* parent = _node->_parent;
        while (parent->_right == _node)
        {
            _node = parent;
            parent = parent->_parent;
        }

        // 找到以后,这个父节点就是当前节点的后继
        if (_node->_right != parent)
        {
            _node = parent;
        }
    }
}

迭代器自减

迭代器 -- 的本质,是找当前节点的中序前驱

这里比自增多一个特殊情况:

  • 如果当前节点在 _head,说明它是 end()
    那么 --end() 应该直接回到最大节点

其余情况和找前驱的常规规则一样:

  • 左子树存在,就去左子树找最右节点
  • 左子树不存在,就向上找第一个把当前节点放在右边的祖先
cpp 复制代码
void _Decreasement()
{
    // 如果当前在头结点位置
    // 说明此时是 end(),做 -- 应该回到最大节点
    if (_node->_parent->_parent == _node && _node->_color == RED)
    {
        _node = _node->_right;
    }
    else if (_node->_left)
    {
        // 如果左子树存在
        // 前驱就是左子树中最右侧的节点
        _node = _node->_left;
        while (_node->_right)
        {
            _node = _node->_right;
        }
    }
    else
    {
        // 如果左子树不存在
        // 就沿着父节点向上找,直到当前节点不是父节点的左孩子
        Node* parent = _node->_parent;
        while (_node == parent->_left)
        {
            _node = parent;
            parent = parent->_parent;
        }

        // 找到以后,这个父节点就是前驱
        _node = parent;
    }
}

Find

查找操作仍然遵循二叉搜索树规则

但这里不能直接比较整个 ValueType,而是要先通过 KeyOfValue 取出 key

这一点很重要,因为后面同一棵红黑树模板还要服务 mapset

  • set 比较的是值本身
  • map 比较的是 pair 里的 first
cpp 复制代码
Iterator Find(const K& key)
{
    KeyOfValue keyOfValue;
    Node* cur = _GetRoot();

    // 按照二叉搜索树规则查找
    while (cur)
    {
        if (key < keyOfValue(cur->_data))
        {
            cur = cur->_left;
        }
        else if (key > keyOfValue(cur->_data))
        {
            cur = cur->_right;
        }
        else
        {
            return Iterator(cur);
        }
    }

    // 没找到就返回 end()
    return End();
}

Clear

清空整棵树时,使用后序遍历最稳妥

因为必须先删左右子树,再删当前节点,否则一旦先删当前节点,下面的孩子就无法继续访问

同时删除完成后,还要把头结点恢复成初始状态

cpp 复制代码
void Clear()
{
    // 先递归释放整棵树
    _Destroy(_GetRoot());

    // 清空后,根节点重新置空
    _head->_parent = nullptr;

    // 最小节点和最大节点重新指向头结点自己
    _head->_left = _head;
    _head->_right = _head;

    // 有效节点个数清零
    _size = 0;
}

void _Destroy(Node* root)
{
    // 空节点直接返回
    if (root == nullptr)
    {
        return;
    }

    // 先释放左子树
    _Destroy(root->_left);

    // 再释放右子树
    _Destroy(root->_right);

    // 最后释放当前节点
    delete root;
}

代码(总)

cpp 复制代码
#include <iostream>
#include <utility>

enum Color
{
    RED,
    BLACK
};

template<class T>
struct RBTreeNode
{
    RBTreeNode(const T& data = T(), Color color = RED)
        : _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _color(color)
    {}

    RBTreeNode<T>* _left;    // 左孩子
    RBTreeNode<T>* _right;   // 右孩子
    RBTreeNode<T>* _parent;  // 父节点
    T _data;                 // 数据域
    Color _color;            // 颜色
};

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
    using Node = RBTreeNode<T>;
    using Self = RBTreeIterator<T, Ref, Ptr>;

    RBTreeIterator(Node* node = nullptr)
        : _node(node)
    {}

    Ref operator*() const
    {
        return _node->_data;
    }

    Ptr operator->() const
    {
        return &_node->_data;
    }

    bool operator!=(const Self& it) const
    {
        return _node != it._node;
    }

    bool operator==(const Self& it) const
    {
        return _node == it._node;
    }

    Self& operator++()
    {
        _Increasement();
        return *this;
    }

    Self operator++(int)
    {
        Self tmp(*this);
        _Increasement();
        return tmp;
    }

    Self& operator--()
    {
        _Decreasement();
        return *this;
    }

    Self operator--(int)
    {
        Self tmp(*this);
        _Decreasement();
        return tmp;
    }

    void _Increasement()
    {
        // 如果右子树存在
        // 后继就是右子树中最左侧的节点
        if (_node->_right)
        {
            _node = _node->_right;
            while (_node->_left)
            {
                _node = _node->_left;
            }
        }
        else
        {
            // 如果右子树不存在
            // 就沿着父节点向上找,直到当前节点不是父节点的右孩子
            Node* parent = _node->_parent;
            while (parent->_right == _node)
            {
                _node = parent;
                parent = parent->_parent;
            }

            // 找到以后,这个父节点就是后继
            if (_node->_right != parent)
            {
                _node = parent;
            }
        }
    }

    void _Decreasement()
    {
        // 如果当前在头结点位置
        // 说明当前是 end(),做 -- 应该回到最大节点
        if (_node->_parent->_parent == _node && _node->_color == RED)
        {
            _node = _node->_right;
        }
        else if (_node->_left)
        {
            // 如果左子树存在
            // 前驱就是左子树中最右侧的节点
            _node = _node->_left;
            while (_node->_right)
            {
                _node = _node->_right;
            }
        }
        else
        {
            // 如果左子树不存在
            // 就沿着父节点向上找,直到当前节点不是父节点的左孩子
            Node* parent = _node->_parent;
            while (_node == parent->_left)
            {
                _node = parent;
                parent = parent->_parent;
            }

            // 找到以后,这个父节点就是前驱
            _node = parent;
        }
    }

    Node* _node;
};

template<class T>
struct Identity
{
    const T& operator()(const T& value) const
    {
        return value;
    }
};

template<class K, class ValueType, class KeyOfValue>
class RBTree
{
    using Node = RBTreeNode<ValueType>;

public:
    using Iterator = RBTreeIterator<ValueType, ValueType&, ValueType*>;

    RBTree()
        : _size(0)
    {
        // 创建头结点
        _head = new Node;

        // 头结点给红色,主要是为了和普通根节点区分
        _head->_color = RED;

        // 初始时没有根节点
        _head->_parent = nullptr;

        // 空树时,最小节点和最大节点都指向头结点自己
        _head->_left = _head;
        _head->_right = _head;
    }

    ~RBTree()
    {
        Clear();
        delete _head;
        _head = nullptr;
    }

    RBTree(const RBTree&) = delete;
    RBTree& operator=(const RBTree&) = delete;

public:
    Iterator Begin()
    {
        // begin() 放在最小节点位置
        return Iterator(_head->_left);
    }

    Iterator End()
    {
        // end() 放在头结点位置
        return Iterator(_head);
    }

    std::pair<Iterator, bool> Insert(const ValueType& data)
    {
        KeyOfValue keyOfValue;
        Node*& root = _GetRoot();

        // 如果当前树为空,直接插入根节点
        // 根节点必须是黑色
        if (root == nullptr)
        {
            root = new Node(data, BLACK);
            root->_parent = _head;
            _head->_left = root;
            _head->_right = root;
            ++_size;
            return std::make_pair(Iterator(root), true);
        }

        // 按照二叉搜索树规则查找插入位置
        Node* parent = root;
        Node* cur = root;
        K key = keyOfValue(data);

        while (cur)
        {
            parent = cur;

            // 如果当前 key 更小,就继续往左走
            if (key < keyOfValue(cur->_data))
            {
                cur = cur->_left;
            }
            // 如果当前 key 更大,就继续往右走
            else if (key > keyOfValue(cur->_data))
            {
                cur = cur->_right;
            }
            // 如果 key 已经存在,就不再插入
            else
            {
                return std::make_pair(Iterator(cur), false);
            }
        }

        // 创建新节点
        // 红黑树中新插入节点默认给红色
        Node* newNode = new Node(data, RED);
        newNode->_parent = parent;

        // 把新节点挂到父节点下面
        if (key < keyOfValue(parent->_data))
        {
            parent->_left = newNode;
        }
        else
        {
            parent->_right = newNode;
        }

        // 如果新节点更小,就更新最小节点
        if (_head->_left == _head || key < keyOfValue(_head->_left->_data))
        {
            _head->_left = newNode;
        }

        // 如果新节点更大,就更新最大节点
        if (_head->_right == _head || key > keyOfValue(_head->_right->_data))
        {
            _head->_right = newNode;
        }

        ++_size;

        // 插入后如果父节点也是红色
        // 就违反了不能有连续红色节点的性质
        cur = newNode;
        parent = cur->_parent;

        while (parent != _head && parent->_color == RED)
        {
            // 既然父节点是红色,那么祖父节点一定存在
            Node* grandfather = parent->_parent;

            // 先处理父节点在祖父左边的情况
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;

                // 叔叔存在并且为红色
                // 只需要变色,然后继续向上处理
                if (uncle && uncle->_color == RED)
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandfather->_color = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    // 如果当前节点在父节点右边
                    // 说明是折线结构,先左旋父节点,转成直线结构
                    if (cur == parent->_right)
                    {
                        _RotateL(parent);
                        std::swap(cur, parent);
                    }

                    // 走到这里一定是直线结构
                    // 父节点染黑,祖父节点染红,然后右旋祖父节点
                    parent->_color = BLACK;
                    grandfather->_color = RED;
                    _RotateR(grandfather);
                    break;
                }
            }
            else
            {
                // 父节点在祖父右边时,处理逻辑和上面对称
                Node* uncle = grandfather->_left;

                if (uncle && uncle->_color == RED)
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandfather->_color = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    if (cur == parent->_left)
                    {
                        _RotateR(parent);
                        std::swap(cur, parent);
                    }

                    parent->_color = BLACK;
                    grandfather->_color = RED;
                    _RotateL(grandfather);
                    break;
                }
            }
        }

        // 无论中间怎么调整,根节点最后都必须是黑色
        root->_color = BLACK;
        root->_parent = _head;

        return std::make_pair(Iterator(newNode), true);
    }

    Iterator Find(const K& key)
    {
        KeyOfValue keyOfValue;
        Node* cur = _GetRoot();

        // 按照二叉搜索树规则查找
        while (cur)
        {
            if (key < keyOfValue(cur->_data))
            {
                cur = cur->_left;
            }
            else if (key > keyOfValue(cur->_data))
            {
                cur = cur->_right;
            }
            else
            {
                return Iterator(cur);
            }
        }

        // 没找到返回 end()
        return End();
    }

    void Clear()
    {
        // 先递归释放整棵树
        _Destroy(_GetRoot());

        // 清空后恢复头结点状态
        _head->_parent = nullptr;
        _head->_left = _head;
        _head->_right = _head;
        _size = 0;
    }

    size_t Size() const
    {
        return _size;
    }

    bool Empty() const
    {
        return _size == 0;
    }

    bool IsValidRBTree() const
    {
        Node* root = _head->_parent;

        // 空树也是红黑树
        if (root == nullptr)
        {
            return true;
        }

        // 根节点必须为黑色
        if (root->_color != BLACK)
        {
            return false;
        }

        // 取最左路径上的黑色节点个数作为基准
        size_t blackCount = 0;
        Node* cur = root;
        while (cur)
        {
            if (cur->_color == BLACK)
            {
                ++blackCount;
            }
            cur = cur->_left;
        }

        size_t pathBlack = 0;
        return _IsValidRBTree(root, pathBlack, blackCount);
    }

private:
    void _RotateL(Node* parent)
    {
        // 记录右孩子和右孩子的左子树
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        Node* ppNode = parent->_parent;

        // 让 parent 接住 subR 的左子树
        parent->_right = subRL;
        if (subRL)
        {
            subRL->_parent = parent;
        }

        // 让 subR 上升,parent 下沉到左边
        subR->_left = parent;
        subR->_parent = ppNode;
        parent->_parent = subR;

        // 把新的子树根重新接回原来的位置
        if (ppNode == _head)
        {
            _head->_parent = subR;
        }
        else if (ppNode->_left == parent)
        {
            ppNode->_left = subR;
        }
        else
        {
            ppNode->_right = subR;
        }
    }

    void _RotateR(Node* parent)
    {
        // 记录左孩子和左孩子的右子树
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        Node* ppNode = parent->_parent;

        // 让 parent 接住 subL 的右子树
        parent->_left = subLR;
        if (subLR)
        {
            subLR->_parent = parent;
        }

        // 让 subL 上升,parent 下沉到右边
        subL->_right = parent;
        subL->_parent = ppNode;
        parent->_parent = subL;

        // 把新的子树根重新接回原来的位置
        if (ppNode == _head)
        {
            _head->_parent = subL;
        }
        else if (ppNode->_left == parent)
        {
            ppNode->_left = subL;
        }
        else
        {
            ppNode->_right = subL;
        }
    }

    bool _IsValidRBTree(Node* root, size_t pathBlack, const size_t blackCount) const
    {
        // 走到空节点时,检查黑色节点个数是否一致
        if (root == nullptr)
        {
            return pathBlack == blackCount;
        }

        // 当前节点如果是黑色,就计入当前路径黑节点个数
        if (root->_color == BLACK)
        {
            ++pathBlack;
        }

        // 如果当前节点和父节点都为红色,就违反性质
        if (root->_parent != _head &&
            root->_parent->_color == RED &&
            root->_color == RED)
        {
            return false;
        }

        return _IsValidRBTree(root->_left, pathBlack, blackCount) &&
               _IsValidRBTree(root->_right, pathBlack, blackCount);
    }

    void _Destroy(Node* root)
    {
        // 空节点直接返回
        if (root == nullptr)
        {
            return;
        }

        // 先释放左子树
        _Destroy(root->_left);

        // 再释放右子树
        _Destroy(root->_right);

        // 最后释放当前节点
        delete root;
    }

    Node*& _GetRoot()
    {
        // 头结点的 parent 域就是根节点
        return _head->_parent;
    }

private:
    Node* _head;  // 头结点
    size_t _size; // 有效节点个数
};

int main()
{
    RBTree<int, int, Identity<int>> t;
    int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };

    for (int x : arr)
    {
        t.Insert(x);
    }

    std::cout << t.IsValidRBTree() << std::endl; // 1
    std::cout << t.Size() << std::endl; // 9

    for (auto it = t.Begin(); it != t.End(); ++it)
    {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    auto pos = t.Find(14);
    if (pos != t.End())
    {
        std::cout << *pos << std::endl; // 14
    }

    auto last = t.End();
    --last;
    std::cout << *last << std::endl; // 26

    t.Clear();
    std::cout << t.Empty() << std::endl; // 1

    return 0;
}

红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log N),红黑树不追 求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数, 所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红 黑树更多。

红黑树的应用

  1. C++ STL库 -- map/set、mutil_map/mutil_set

  2. Java 库

  3. linux内核

  4. 其他一些库

红黑树模拟实现STL中的map与set

红黑树的迭代器

STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后, 可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位 置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块? 能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行--操作,必须要能找最 后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置:

红黑树模拟实现 STL 中的 map 与 set

这一部分的核心目标,不是重新写一套新的平衡树,而是把前面已经实现好的红黑树继续抽象,让它能够同时作为 mapset 的底层结构

这里最关键的矛盾是:

  • set 中存的是 key
  • map 中存的是 pair<K, V>

但是这两个容器底层都希望复用同一棵红黑树

所以红黑树必须继续改造,不能再简单写成"存什么就比较什么",而要改成:

  • 节点里存的是 ValueType
  • 真正参与比较的是 key
  • key 如何从 ValueType 中取出来,由 KeyOfValue 决定

红黑树的迭代器

这一部分主要解决两个问题:

  • begin()end() 应该放在哪里
  • 迭代器的 ++-- 应该如何移动

红黑树本质上还是二叉搜索树,所以对它做中序遍历,得到的一定是一个有序序列

因此:

  • begin() 应该放在最小节点,也就是最左侧节点
  • end() 不能简单放成 nullptr
  • 最适合把 end() 放在头结点 _head

为什么 end() 不能给成 nullptr?因为 STL 中允许对 end()--,也就是要求 --end() 能回到最后一个有效元素,如果 end() 是空指针,就无法从空指针再找到最大节点

所以才会专门给红黑树增加一个头结点 _head

  • _head->_parent 指向根节点
  • _head->_left 指向最小节点
  • _head->_right 指向最大节点

这样:

  • begin() 就能直接返回最小节点
  • end() 就能直接返回头结点
  • --end() 就能直接回到最大节点
迭代器定义
cpp 复制代码
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
    using Node = RBTreeNode<T>;
    using Self = RBTreeIterator<T, Ref, Ptr>;

    RBTreeIterator(Node* node = nullptr)
        : _node(node)
    {}

    Ref operator*() const
    {
        return _node->_data;
    }

    Ptr operator->() const
    {
        return &_node->_data;
    }

    bool operator!=(const Self& it) const
    {
        return _node != it._node;
    }

    bool operator==(const Self& it) const
    {
        return _node == it._node;
    }

    Self& operator++()
    {
        _Increasement();
        return *this;
    }

    Self operator++(int)
    {
        Self tmp(*this);
        _Increasement();
        return tmp;
    }

    Self& operator--()
    {
        _Decreasement();
        return *this;
    }

    Self operator--(int)
    {
        Self tmp(*this);
        _Decreasement();
        return tmp;
    }

    Node* _node;
};
operator++ 的实现思路

迭代器自增,本质上是在找当前节点的中序后继

分两种情况:

  • 当前节点右子树存在

    后继就是右子树中最左侧的节点

  • 当前节点右子树不存在

    就沿着父节点向上找,直到找到一个祖先,使当前节点处于它的左侧,那么这个祖先就是后继

_Increasement
cpp 复制代码
void _Increasement()
{
    // 如果右子树存在
    // 后继一定在右子树中,并且是右子树里最左侧的节点
    if (_node->_right)
    {
        _node = _node->_right;
        while (_node->_left)
        {
            _node = _node->_left;
        }
    }
    else
    {
        // 如果右子树不存在
        // 就沿着父节点向上找,直到当前节点不是父节点的右孩子
        Node* parent = _node->_parent;
        while (parent->_right == _node)
        {
            _node = parent;
            parent = parent->_parent;
        }

        // 找到以后,这个父节点就是当前节点的后继
        if (_node->_right != parent)
        {
            _node = parent;
        }
    }
}
operator-- 的实现思路

迭代器自减,本质上是在找当前节点的中序前驱

分三种情况:

  • 当前节点在头结点位置

    说明当前是 end(),此时 -- 应该直接回到最大节点

  • 当前节点左子树存在

    前驱就是左子树中最右侧的节点

  • 当前节点左子树不存在

    就沿着父节点向上找,直到找到一个祖先,使当前节点处于它的右侧,那么这个祖先就是前驱

_Decreasement
cpp 复制代码
void _Decreasement()
{
    // 如果当前在头结点位置
    // 说明当前是 end(),做 -- 应该回到整棵树中最大的节点
    if (_node->_parent->_parent == _node && _node->_color == RED)
    {
        _node = _node->_right;
    }
    else if (_node->_left)
    {
        // 如果左子树存在
        // 前驱就是左子树中最右侧的节点
        _node = _node->_left;
        while (_node->_right)
        {
            _node = _node->_right;
        }
    }
    else
    {
        // 如果左子树不存在
        // 就沿着父节点向上找,直到当前节点不是父节点的左孩子
        Node* parent = _node->_parent;
        while (_node == parent->_left)
        {
            _node = parent;
            parent = parent->_parent;
        }

        // 找到以后,这个父节点就是当前节点的前驱
        _node = parent;
    }
}

改造红黑树

这一部分的重点,是把前面"只能存一种数据"的红黑树,改造成能服务关联式容器的通用红黑树

思路:

  • K 表示真正的 key 类型
  • ValueType 表示节点中真正存储的数据类型
  • KeyOfValue 表示一个仿函数,用来从 ValueType 中取出 key

这样设计之后:

  • setValueType 就是 K
  • mapValueType 就是 pair<K, V>

红黑树不再关心你节点里到底存的是 K 还是 pair<K, V>

它只关心一件事:

如何从节点保存的数据中取出 key,并用 key 来比较

改造后的红黑树模板
cpp 复制代码
template<class K, class ValueType, class KeyOfValue>
class RBTree
{
    using Node = RBTreeNode<ValueType>;

public:
    using Iterator = RBTreeIterator<ValueType, ValueType&, ValueType*>;

    RBTree()
        : _size(0)
    {
        // 创建头结点
        _head = new Node;

        // 头结点给红色,主要用于和普通节点区分
        _head->_color = RED;

        // 初始时还没有根节点
        _head->_parent = nullptr;

        // 空树时,最小节点和最大节点都先指向头结点自己
        _head->_left = _head;
        _head->_right = _head;
    }

    ~RBTree()
    {
        Clear();
        delete _head;
        _head = nullptr;
    }

private:
    Node* _head;  // 头结点
    size_t _size; // 有效节点个数
};
Begin

Begin() 返回的是最小节点位置

cpp 复制代码
Iterator Begin()
{
    // begin() 指向整棵树中最小的节点
    return Iterator(_head->_left);
}
End

End() 返回的是头结点位置

cpp 复制代码
Iterator End()
{
    // end() 放在头结点位置
    return Iterator(_head);
}
Insert

比较时不能直接比较整个 ValueType,必须先通过 KeyOfValue 取出 key 再比较

这正是"支持 map/set 共用一套底层"的关键

cpp 复制代码
std::pair<Iterator, bool> Insert(const ValueType& data)
{
    KeyOfValue keyOfValue;
    Node*& root = _GetRoot();

    // 如果树为空,直接插入根节点
    if (root == nullptr)
    {
        root = new Node(data, BLACK);
        root->_parent = _head;
        _head->_left = root;
        _head->_right = root;
        ++_size;
        return std::make_pair(Iterator(root), true);
    }

    // 先取出待插入数据对应的 key
    K key = keyOfValue(data);

    // 按照二叉搜索树规则查找插入位置
    Node* parent = root;
    Node* cur = root;

    while (cur)
    {
        parent = cur;

        // 比较时比较的是 key,不是整个 ValueType
        if (key < keyOfValue(cur->_data))
        {
            cur = cur->_left;
        }
        else if (key > keyOfValue(cur->_data))
        {
            cur = cur->_right;
        }
        else
        {
            // key 相同,说明元素已存在
            return std::make_pair(Iterator(cur), false);
        }
    }

    // 创建新节点,新节点默认给红色
    Node* newNode = new Node(data, RED);
    newNode->_parent = parent;

    // 把新节点挂到父节点下面
    if (key < keyOfValue(parent->_data))
    {
        parent->_left = newNode;
    }
    else
    {
        parent->_right = newNode;
    }

    // 如果新节点更小,就更新最小节点
    if (_head->_left == _head || key < keyOfValue(_head->_left->_data))
    {
        _head->_left = newNode;
    }

    // 如果新节点更大,就更新最大节点
    if (_head->_right == _head || key > keyOfValue(_head->_right->_data))
    {
        _head->_right = newNode;
    }

    ++_size;

    // 下面这部分仍然是红黑树插入调整
    // 如果父节点也是红色,就违反了不能有连续红色节点的性质
    cur = newNode;
    parent = cur->_parent;

    while (parent != _head && parent->_color == RED)
    {
        Node* grandfather = parent->_parent;

        if (parent == grandfather->_left)
        {
            Node* uncle = grandfather->_right;

            // 叔叔存在并且为红色
            // 这种情况只需要变色,然后继续向上处理祖父节点
            if (uncle && uncle->_color == RED)
            {
                parent->_color = BLACK;
                uncle->_color = BLACK;
                grandfather->_color = RED;

                cur = grandfather;
                parent = cur->_parent;
            }
            else
            {
                // 如果当前节点在父节点右边
                // 说明是折线结构,先左旋父节点,把它转成直线结构
                if (cur == parent->_right)
                {
                    _RotateL(parent);
                    std::swap(cur, parent);
                }

                // 走到这里一定是直线结构
                // 父节点染黑,祖父节点染红,然后右旋祖父节点
                parent->_color = BLACK;
                grandfather->_color = RED;
                _RotateR(grandfather);
                break;
            }
        }
        else
        {
            Node* uncle = grandfather->_left;

            if (uncle && uncle->_color == RED)
            {
                parent->_color = BLACK;
                uncle->_color = BLACK;
                grandfather->_color = RED;

                cur = grandfather;
                parent = cur->_parent;
            }
            else
            {
                if (cur == parent->_left)
                {
                    _RotateR(parent);
                    std::swap(cur, parent);
                }

                parent->_color = BLACK;
                grandfather->_color = RED;
                _RotateL(grandfather);
                break;
            }
        }
    }

    // 根节点最后必须保持黑色
    root->_color = BLACK;
    root->_parent = _head;

    return std::make_pair(Iterator(newNode), true);
}
Find

查找和插入的查找过程一样,关键也在于:先取 key,再比较

cpp 复制代码
Iterator Find(const K& key)
{
    KeyOfValue keyOfValue;
    Node* cur = _GetRoot();

    // 按照二叉搜索树规则查找
    while (cur)
    {
        if (key < keyOfValue(cur->_data))
        {
            cur = cur->_left;
        }
        else if (key > keyOfValue(cur->_data))
        {
            cur = cur->_right;
        }
        else
        {
            return Iterator(cur);
        }
    }

    // 没找到就返回 end()
    return End();
}

map 的模拟实现

思想:

map 的底层本来就是红黑树,所以只需要在 map 内部封装一棵合适类型的红黑树,再把接口简单包装一下即可

这里节点里真正存储的是:

cpp 复制代码
pair<K, V>

但比较大小时,比较的是 pair.first

所以这里需要定义一个专门给 map 使用的 KeyOfValue

MapKeyOfValue
cpp 复制代码
template<class K, class V>
struct MapKeyOfValue
{
    const K& operator()(const std::pair<K, V>& value) const
    {
        // map 比较大小时,只看键值对中的 first
        return value.first;
    }
};
map 的类定义
cpp 复制代码
template<class K, class V>
class map
{
    using ValueType = std::pair<K, V>;
    using RBTreeType = RBTree<K, ValueType, MapKeyOfValue<K, V>>;

public:
    using iterator = typename RBTreeType::Iterator;

private:
    RBTreeType _tree;
};
begin
cpp 复制代码
iterator begin()
{
    // 直接调用底层红黑树的 Begin
    return _tree.Begin();
}
end
cpp 复制代码
iterator end()
{
    // 直接调用底层红黑树的 End
    return _tree.End();
}
insert

map 插入的本质,就是向底层红黑树插入一个键值对

cpp 复制代码
std::pair<iterator, bool> insert(const ValueType& data)
{
    // 直接把键值对交给底层红黑树插入
    return _tree.Insert(data);
}
find

查找时只需要传入 key

cpp 复制代码
iterator find(const K& key)
{
    // 底层红黑树支持按 key 查找
    return _tree.Find(key);
}
operator[]

这是 map 最有代表性的接口,也是面试和源码分析里最常问的地方

它的行为是:

  • 如果 key 已存在,就返回该节点中的 second
  • 如果 key 不存在,就先插入一个 pair(key, V())
  • 然后再返回这个节点中的 second

所以它实际上是"插入 + 定位 + 取值"的组合

cpp 复制代码
V& operator[](const K& key)
{
    // 如果 key 不存在,会插入 pair(key, V())
    // 如果 key 已存在,会直接返回原来的位置
    // 无论哪种情况,Insert 返回值里的 first 都是对应节点的迭代器
    // 对这个迭代器解引用后得到 pair<K, V>
    // 再取 second,就拿到了 value
    return (*(_tree.Insert(ValueType(key, V())).first)).second;
}
其他辅助接口
cpp 复制代码
void clear()
{
    _tree.Clear();
}

size_t size() const
{
    return _tree.Size();
}

bool empty() const
{
    return _tree.Empty();
}

set 的模拟实现

setmap 更简单,因为它保存的就是 key 本身

所以:

  • ValueType 就是 K
  • KeyOfValue 直接把参数本身返回即可

这里通过 set 再次说明:

同一棵红黑树模板,只要换掉 ValueTypeKeyOfValue,就能支持不同容器

SetKeyOfValue
cpp 复制代码
template<class K>
struct SetKeyOfValue
{
    const K& operator()(const K& key) const
    {
        // set 中存的就是 key 本身
        // 直接返回即可
        return key;
    }
};
set 的类定义
cpp 复制代码
template<class K>
class set
{
    using ValueType = K;
    using RBTreeType = RBTree<K, ValueType, SetKeyOfValue<K>>;

public:
    using iterator = typename RBTreeType::Iterator;

private:
    RBTreeType _tree;
};
begin
cpp 复制代码
iterator begin()
{
    // 直接调用底层红黑树的 Begin
    return _tree.Begin();
}
end
cpp 复制代码
iterator end()
{
    // 直接调用底层红黑树的 End
    return _tree.End();
}
insert

set 的插入,本质上就是向底层红黑树插入一个 key

cpp 复制代码
std::pair<iterator, bool> insert(const ValueType& data)
{
    // 直接把元素交给底层红黑树插入
    return _tree.Insert(data);
}
find
cpp 复制代码
iterator find(const K& key)
{
    // 直接调用底层红黑树查找
    return _tree.Find(key);
}
其他辅助接口
cpp 复制代码
void clear()
{
    _tree.Clear();
}

size_t size() const
{
    return _tree.Size();
}

bool empty() const
{
    return _tree.Empty();
}

完整代码

cpp 复制代码
#include <iostream>
#include <string>
#include <utility>

enum Color
{
    RED,
    BLACK
};

template<class T>
struct RBTreeNode
{
    RBTreeNode(const T& data = T(), Color color = RED)
        : _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _color(color)
    {}

    RBTreeNode<T>* _left;    // 左孩子
    RBTreeNode<T>* _right;   // 右孩子
    RBTreeNode<T>* _parent;  // 父节点
    T _data;                 // 数据域
    Color _color;            // 颜色
};

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
    using Node = RBTreeNode<T>;
    using Self = RBTreeIterator<T, Ref, Ptr>;

    RBTreeIterator(Node* node = nullptr)
        : _node(node)
    {}

    Ref operator*() const
    {
        return _node->_data;
    }

    Ptr operator->() const
    {
        return &_node->_data;
    }

    bool operator!=(const Self& it) const
    {
        return _node != it._node;
    }

    bool operator==(const Self& it) const
    {
        return _node == it._node;
    }

    Self& operator++()
    {
        _Increasement();
        return *this;
    }

    Self operator++(int)
    {
        Self tmp(*this);
        _Increasement();
        return tmp;
    }

    Self& operator--()
    {
        _Decreasement();
        return *this;
    }

    Self operator--(int)
    {
        Self tmp(*this);
        _Decreasement();
        return tmp;
    }

    void _Increasement()
    {
        // 如果右子树存在
        // 后继一定在右子树中,并且是右子树里最左侧的节点
        if (_node->_right)
        {
            _node = _node->_right;
            while (_node->_left)
            {
                _node = _node->_left;
            }
        }
        else
        {
            // 如果右子树不存在
            // 就沿着父节点向上找,直到当前节点不是父节点的右孩子
            Node* parent = _node->_parent;
            while (parent->_right == _node)
            {
                _node = parent;
                parent = parent->_parent;
            }

            // 找到以后,这个父节点就是当前节点的后继
            if (_node->_right != parent)
            {
                _node = parent;
            }
        }
    }

    void _Decreasement()
    {
        // 如果当前在头结点位置
        // 说明当前是 end(),做 -- 应该回到整棵树中最大的节点
        if (_node->_parent->_parent == _node && _node->_color == RED)
        {
            _node = _node->_right;
        }
        else if (_node->_left)
        {
            // 如果左子树存在
            // 前驱就是左子树中最右侧的节点
            _node = _node->_left;
            while (_node->_right)
            {
                _node = _node->_right;
            }
        }
        else
        {
            // 如果左子树不存在
            // 就沿着父节点向上找,直到当前节点不是父节点的左孩子
            Node* parent = _node->_parent;
            while (_node == parent->_left)
            {
                _node = parent;
                parent = parent->_parent;
            }

            // 找到以后,这个父节点就是当前节点的前驱
            _node = parent;
        }
    }

    Node* _node;
};

template<class K, class ValueType, class KeyOfValue>
class RBTree
{
    using Node = RBTreeNode<ValueType>;

public:
    using Iterator = RBTreeIterator<ValueType, ValueType&, ValueType*>;

    RBTree()
        : _size(0)
    {
        // 创建头结点
        _head = new Node;

        // 头结点颜色给红色,主要用于和普通节点区分
        _head->_color = RED;

        // 初始时还没有根节点
        _head->_parent = nullptr;

        // 空树时,最小节点和最大节点都先指向头结点自己
        _head->_left = _head;
        _head->_right = _head;
    }

    ~RBTree()
    {
        Clear();
        delete _head;
        _head = nullptr;
    }

    RBTree(const RBTree&) = delete;
    RBTree& operator=(const RBTree&) = delete;

public:
    Iterator Begin()
    {
        // begin() 指向最小节点
        return Iterator(_head->_left);
    }

    Iterator End()
    {
        // end() 放在头结点位置
        return Iterator(_head);
    }

    std::pair<Iterator, bool> Insert(const ValueType& data)
    {
        KeyOfValue keyOfValue;
        Node*& root = _GetRoot();

        // 如果当前树为空,直接插入根节点
        // 根节点必须为黑色
        if (root == nullptr)
        {
            root = new Node(data, BLACK);
            root->_parent = _head;
            _head->_left = root;
            _head->_right = root;
            ++_size;
            return std::make_pair(Iterator(root), true);
        }

        // 先提取待插入数据的 key
        K key = keyOfValue(data);

        // 按照二叉搜索树规则查找插入位置
        Node* parent = root;
        Node* cur = root;

        while (cur)
        {
            parent = cur;

            // 比较时比较的是 key,而不是整个 ValueType
            if (key < keyOfValue(cur->_data))
            {
                cur = cur->_left;
            }
            else if (key > keyOfValue(cur->_data))
            {
                cur = cur->_right;
            }
            else
            {
                // key 已存在,说明不能重复插入
                return std::make_pair(Iterator(cur), false);
            }
        }

        // 创建新节点
        // 红黑树中新插入节点默认给红色
        Node* newNode = new Node(data, RED);
        newNode->_parent = parent;

        // 把新节点挂接到父节点下面
        if (key < keyOfValue(parent->_data))
        {
            parent->_left = newNode;
        }
        else
        {
            parent->_right = newNode;
        }

        // 如果新节点更小,就更新最小节点
        if (_head->_left == _head || key < keyOfValue(_head->_left->_data))
        {
            _head->_left = newNode;
        }

        // 如果新节点更大,就更新最大节点
        if (_head->_right == _head || key > keyOfValue(_head->_right->_data))
        {
            _head->_right = newNode;
        }

        ++_size;

        // 插入后开始做红黑树调整
        // 如果父节点也是红色,就违反了不能有连续红色节点的性质
        cur = newNode;
        parent = cur->_parent;

        while (parent != _head && parent->_color == RED)
        {
            // 既然父节点是红色,那么祖父节点一定存在
            Node* grandfather = parent->_parent;

            // 先处理父节点在祖父左边的情况
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;

                // 叔叔存在并且为红色
                // 这种情况只需要变色,然后继续向上处理祖父节点
                if (uncle && uncle->_color == RED)
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandfather->_color = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    // 如果当前节点在父节点右边
                    // 说明形成了折线结构,先左旋父节点,把它转成直线结构
                    if (cur == parent->_right)
                    {
                        _RotateL(parent);
                        std::swap(cur, parent);
                    }

                    // 走到这里,一定已经转成直线结构
                    // 父节点染黑,祖父节点染红,然后右旋祖父节点
                    parent->_color = BLACK;
                    grandfather->_color = RED;
                    _RotateR(grandfather);
                    break;
                }
            }
            else
            {
                // 父节点在祖父右边时,处理逻辑完全对称
                Node* uncle = grandfather->_left;

                if (uncle && uncle->_color == RED)
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandfather->_color = RED;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    if (cur == parent->_left)
                    {
                        _RotateR(parent);
                        std::swap(cur, parent);
                    }

                    parent->_color = BLACK;
                    grandfather->_color = RED;
                    _RotateL(grandfather);
                    break;
                }
            }
        }

        // 无论中间怎么调整,根节点最后都必须是黑色
        root->_color = BLACK;
        root->_parent = _head;

        return std::make_pair(Iterator(newNode), true);
    }

    Iterator Find(const K& key)
    {
        KeyOfValue keyOfValue;
        Node* cur = _GetRoot();

        // 按照二叉搜索树规则查找
        while (cur)
        {
            if (key < keyOfValue(cur->_data))
            {
                cur = cur->_left;
            }
            else if (key > keyOfValue(cur->_data))
            {
                cur = cur->_right;
            }
            else
            {
                return Iterator(cur);
            }
        }

        // 没找到返回 end()
        return End();
    }

    void Clear()
    {
        // 先递归释放整棵树
        _Destroy(_GetRoot());

        // 清空后把头结点恢复成初始状态
        _head->_parent = nullptr;
        _head->_left = _head;
        _head->_right = _head;
        _size = 0;
    }

    size_t Size() const
    {
        return _size;
    }

    bool Empty() const
    {
        return _size == 0;
    }

private:
    void _RotateL(Node* parent)
    {
        // 记录右孩子和右孩子的左子树
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        Node* ppNode = parent->_parent;

        // 让 parent 接住 subR 的左子树
        parent->_right = subRL;
        if (subRL)
        {
            subRL->_parent = parent;
        }

        // 让 subR 上升,parent 下沉到左边
        subR->_left = parent;
        subR->_parent = ppNode;
        parent->_parent = subR;

        // 把新的子树根重新接回原来的位置
        if (ppNode == _head)
        {
            _head->_parent = subR;
        }
        else if (ppNode->_left == parent)
        {
            ppNode->_left = subR;
        }
        else
        {
            ppNode->_right = subR;
        }
    }

    void _RotateR(Node* parent)
    {
        // 记录左孩子和左孩子的右子树
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        Node* ppNode = parent->_parent;

        // 让 parent 接住 subL 的右子树
        parent->_left = subLR;
        if (subLR)
        {
            subLR->_parent = parent;
        }

        // 让 subL 上升,parent 下沉到右边
        subL->_right = parent;
        subL->_parent = ppNode;
        parent->_parent = subL;

        // 把新的子树根重新接回原来的位置
        if (ppNode == _head)
        {
            _head->_parent = subL;
        }
        else if (ppNode->_left == parent)
        {
            ppNode->_left = subL;
        }
        else
        {
            ppNode->_right = subL;
        }
    }

    void _Destroy(Node* root)
    {
        // 空节点直接返回
        if (root == nullptr)
        {
            return;
        }

        // 先释放左子树
        _Destroy(root->_left);

        // 再释放右子树
        _Destroy(root->_right);

        // 最后释放当前节点
        delete root;
    }

    Node*& _GetRoot()
    {
        // 头结点的 parent 域就是根节点
        return _head->_parent;
    }

private:
    Node* _head;  // 头结点
    size_t _size; // 有效节点个数
};

template<class K, class V>
struct MapKeyOfValue
{
    const K& operator()(const std::pair<K, V>& value) const
    {
        // map 比较大小时,只比较 pair 中的 first
        return value.first;
    }
};

template<class K>
struct SetKeyOfValue
{
    const K& operator()(const K& key) const
    {
        // set 中存的就是 key 本身,直接返回即可
        return key;
    }
};

namespace bite
{
    template<class K, class V>
    class map
    {
        using ValueType = std::pair<K, V>;
        using RBTreeType = RBTree<K, ValueType, MapKeyOfValue<K, V>>;

    public:
        using iterator = typename RBTreeType::Iterator;

        iterator begin()
        {
            // 直接复用底层红黑树的 begin
            return _tree.Begin();
        }

        iterator end()
        {
            // 直接复用底层红黑树的 end
            return _tree.End();
        }

        std::pair<iterator, bool> insert(const ValueType& data)
        {
            // map 插入,本质上就是向底层红黑树插入一个 pair<K, V>
            return _tree.Insert(data);
        }

        iterator find(const K& key)
        {
            // 查找时只需要传 key
            return _tree.Find(key);
        }

        void clear()
        {
            _tree.Clear();
        }

        size_t size() const
        {
            return _tree.Size();
        }

        bool empty() const
        {
            return _tree.Empty();
        }

        V& operator[](const K& key)
        {
            // 如果 key 不存在,会插入 pair(key, V())
            // 如果 key 已存在,会直接返回原有位置
            // Insert 返回值中的 first 是对应节点的迭代器
            // 对迭代器解引用后得到 pair<K, V>,再取 second 就是 value
            return (*(_tree.Insert(ValueType(key, V())).first)).second;
        }

    private:
        RBTreeType _tree;
    };

    template<class K>
    class set
    {
        using ValueType = K;
        using RBTreeType = RBTree<K, ValueType, SetKeyOfValue<K>>;

    public:
        using iterator = typename RBTreeType::Iterator;

        iterator begin()
        {
            // 直接复用底层红黑树的 begin
            return _tree.Begin();
        }

        iterator end()
        {
            // 直接复用底层红黑树的 end
            return _tree.End();
        }

        std::pair<iterator, bool> insert(const ValueType& data)
        {
            // set 插入,本质上就是向底层红黑树插入一个 key
            return _tree.Insert(data);
        }

        iterator find(const K& key)
        {
            // 查找时只需要传 key
            return _tree.Find(key);
        }

        void clear()
        {
            _tree.Clear();
        }

        size_t size() const
        {
            return _tree.Size();
        }

        bool empty() const
        {
            return _tree.Empty();
        }

    private:
        RBTreeType _tree;
    };
}

int main()
{
    bite::map<std::string, int> mp;

    mp.insert(std::make_pair("apple", 3));
    mp.insert(std::make_pair("banana", 5));
    mp["orange"] = 8;
    mp["apple"]++;

    std::cout << mp.size() << std::endl; // 3
    std::cout << mp["apple"] << std::endl; // 4
    std::cout << mp["banana"] << std::endl; // 5
    std::cout << mp["orange"] << std::endl; // 8

    for (auto it = mp.begin(); it != mp.end(); ++it)
    {
        std::cout << it->first << ":" << it->second << " ";
    }
    std::cout << std::endl;

    auto pos1 = mp.find("banana");
    if (pos1 != mp.end())
    {
        std::cout << pos1->first << ":" << pos1->second << std::endl; // banana:5
    }

    bite::set<int> st;
    st.insert(10);
    st.insert(3);
    st.insert(7);
    st.insert(3);
    st.insert(15);

    std::cout << st.size() << std::endl; // 4

    for (auto it = st.begin(); it != st.end(); ++it)
    {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    auto pos2 = st.find(7);
    if (pos2 != st.end())
    {
        std::cout << *pos2 << std::endl; // 7
    }

    return 0;
}
相关推荐
IronMurphy1 小时前
【算法五十八】23. 合并 K 个升序链表
数据结构·算法·链表
思茂信息1 小时前
CST软件基于液态金属开关的方向图可重构天线
服务器·算法·重构·cst·仿真软件·电磁仿真
凡人叶枫2 小时前
Effective C++ 条款39:明智而审慎地使用 private 继承
java·数据库·c++·嵌入式开发
月疯2 小时前
PPG研究中暑的算法记录
算法
不想写代码的星星2 小时前
伪共享:逻辑无共享,物理打成狗
c++
春日见2 小时前
vscode的AI编程插件推荐:
大数据·ide·vscode·算法·机器学习·编辑器·ai编程
blxr_2 小时前
力扣hot100路径总和Ⅲhttps://leetcode.cn/problems/path-sum-iii/
算法·leetcode·职场和发展
noipp2 小时前
【无标题】
c语言·数据结构·c++·算法
森G2 小时前
64、完善聊天室程序(TLV拓展)---------网络编程
网络·c++·tcp/ip