欢迎光临
我们一直在努力

【C++】 红黑树封装 STL set/map 超详细解析


  红黑树封装 STL set/map 超详细解析  

一、STL 为什么用红黑树封装 set/map?

满足 set/map 的核心需求:

  • key 必须有序(红黑树是二叉搜索树,中序遍历有序)
  • key 必须唯一(set 元素唯一,map 键唯一)
  • 高效增删改查(O(logN))
  • 迭代器不失效(除删除节点外,其他迭代器永久有效)
  • STL 设计思想:红黑树作为通用底层结构,set 和 map 只是它的 “适配器”,就像栈 / 队列适配 deque 一样。

    二、红黑树封装 set/map 核心原理(最关键)

    1. 底层共用一棵红黑树

    C++ STL 中:

    • set:存储唯一有序的 key
    • map:存储唯一有序的 key + 对应 value

    它们底层共用同一个红黑树模板,只通过存储数据类型区分:

    • set 红黑树节点:存储 Key
    • map 红黑树节点:存储 pair<const Key, Value>

    2. 封装核心:萃取器(key_of_value)

            红黑树只需要key 进行排序和比较,但 map 存的是键值对,因此需要一个 “萃取器” 告诉红黑树:

    • 给我你的数据,我从中提取出 key 用于排序。

            这是 set 和 map 唯一的本质区别!

    容器存储类型key 提取方式
    set Key 直接返回自身
    map pair<const Key, T> 返回 pair.first

    三、set /map/multiset /multimap 封装区别

    容器底层红黑树key 唯一性存储结构
    set 红黑树 唯一 Key
    map 红黑树 唯一 pair<const Key, T>
    multiset 红黑树 可重复 Key
    multimap 红黑树 可重复 pair<const Key, T>

    唯一区别:插入时是否检查 key 重复,其他逻辑完全一致。


    四、STL 底层源码结构

    1. 红黑树模板(通用底层)

    // 简化版 STL 红黑树
    template <class Key, // 键类型
    class Value, // 存储值类型(set=Key, map=pair<Key,T>)
    class KeyOfValue, // key 萃取器:从 Value 中提取 Key
    class Compare> // 比较函数(默认 less<Key>)
    class rb_tree {
    public:
    // 核心接口(set/map 直接调用)
    iterator insert(const Value& val); // 插入
    iterator find(const Key& key); // 查找
    void erase(iterator pos); // 删除
    // … 其他接口
    };

    2. set 封装(适配器)

    template <class Key, class Compare = less<Key>>
    class set {
    public:
    // 定义萃取器:Value 就是 Key,直接返回
    struct KeyOfValue {
    const Key& operator()(const Key& k) { return k; }
    };

    private:
    // 底层红黑树
    using RepType = rb_tree<Key, Key, KeyOfValue, Compare>;
    RepType tree; // 聚合实现,不是继承!

    public:
    // 接口直接转发给红黑树
    pair<iterator, bool> insert(const Key& k) {
    return tree.insert(k);
    }
    iterator find(const Key& k) { return tree.find(k); }
    // … 其他接口
    };

    3. map 封装(适配器)

    template <class Key, class T, class Compare = less<Key>>
    class map {
    public:
    using ValueType = pair<const Key, T>;
    // 萃取器:从 pair 中提取 first 作为 key
    struct KeyOfValue {
    const Key& operator()(const ValueType& v) { return v.first; }
    };

    private:
    using RepType = rb_tree<Key, ValueType, KeyOfValue, Compare>;
    RepType tree;

    public:
    // 转发接口
    pair<iterator, bool> insert(const ValueType& v) {
    return tree.insert(v);
    }
    // [] 运算符重载:map 独有
    T& operator[](const Key& k) {
    return (*insert({k, T()}).first).second;
    }
    // …
    };


    五、set 和 map 的关键差异总结(面试必问)

  • 存储结构不同
    • set:只存 key
    • map:存 pair<const key, value>
  • key 萃取方式不同
    • set:  数据本身就是 key
    • map:从 pair 中取 first 作为 key
  • 接口不同
    • map 支持 [] 运算符,set 不支持
  • 底层完全相同
    • 都是红黑树适配,性能完全一致
  • 六、红黑树封装的优势

  • 代码复用:一套红黑树实现 4 个容器(set/map/multiset/multimap)
  • 稳定性能:O (logN) 所有操作,不会退化
  • 有序性:中序遍历天然有序,支持范围查找
  • 迭代器稳定:插入不失效,删除仅当前节点失效
  • 七、常见面试题

  • set 和 map 底层是什么?红黑树。
  • 为什么不用哈希表?红黑树有序,哈希表无序;红黑树稳定 O (logN),哈希表有哈希冲突。
  • set 可以修改元素吗?不可以,会破坏红黑树结构,元素是 const 的。
  • map 的 key 为什么是 const?防止修改 key 破坏红黑树排序规则。
  • multiset 和 set 区别?插入时不检查 key 重复。

  • 八、代码实现

    RBTree.h

    enum Colour
    {
    RED,
    BLACK
    };
    template<class T>
    struct RBTreeNode
    {
    T _data;
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    Colour _col;
    RBTreeNode(const T& data)
    :_data(data)
    , _left(nullptr)
    , _right(nullptr)
    , _parent(nullptr)
    {}
    };

    // 实现步骤:
    // 1、实现红⿊树
    // 2、封装map和set框架,解决KeyOfT
    // 3、iterator
    // 4、const_iterator
    // 5、key不⽀持修改的问题
    // 6、operator[]

    template<class T, class Ref, class Ptr>
    struct TreeIterator
    {
    typedef RBTreeNode<T> Node;
    typedef TreeIterator<T, Ref, Ptr> Self;
    Node* _node;
    private:
    Node* _root = nullptr;
    public:
    TreeIterator(Node* node)
    :_node(node)
    {}

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

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

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

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

    Self& operator–()
    {
    if (_node == nullptr)//如果当前节点为空,⾛到中序最后⼀个结点,整棵树的最右结点
    {
    Node* max = _root;
    while (max && max->_right)
    {
    max = max->_right;
    }

    _node = max;
    }
    else if (_node->_left)// 左⼦树不为空,中序左⼦树最后⼀个
    {
    Node* max = _node->_left;
    while (max->_right)
    {
    max = max->_right;
    }

    _node = max;
    }
    else
    {
    Node* cur = _node;
    Node* parent = cur->_parent;
    while (parent && cur == parent->_left)
    {
    cur = parent;
    parent = parent->_parent;
    }

    _node = parent;
    }
    return *this;
    }

    Self& operator++()
    {
    // 当前节点右不为空,下一个就是右子树的中序第一个(最左节点)
    if (_node->_right)
    {
    Node* min = _node->_right;
    while (min->_left)
    {
    min = min->_left;
    }

    _node = min;
    }
    else// 当前节点右为空,下一个孩子是父亲左的那个祖先节点
    {
    Node* cur = _node;
    Node* parent = cur->_parent;
    while (parent && cur == parent->_right)
    {
    cur = parent;
    parent = parent->_parent;
    }

    _node = parent;
    }

    return *this;
    }
    };

    template<class K,class T,class KeyOfT>
    class RBTree
    {
    typedef RBTreeNode<T> Node;
    public:
    typedef TreeIterator<T, T&, T*> Iterator;
    typedef TreeIterator<T, const T&, const T*> ConstIterator;
    private:
    Node* _root = nullptr;
    public:
    Iterator Begin()
    {
    Node* min = _root;
    while (min && min->_left)
    {
    min = min->_left;
    }

    return Iterator(min);
    }

    Iterator End()
    {
    return Iterator(nullptr);
    }

    pair<Iterator, bool> Insert(const T& data)
    {
    if (_root == nullptr)
    {
    _root = new Node(data);
    _root->_col = BLACK;

    return { Iterator(_root), true };
    }
    KeyOfT kot;
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
    if (kot(cur->_data)< kot(data))
    {
    parent = cur;
    cur = cur->_right;
    }
    else if (kot(cur->_data) > kot(data))
    {
    parent = cur;
    cur = cur->_left;
    }
    else
    {
    return { Iterator(cur), false };
    }
    }

    cur = new Node(data);
    Node* newnode = cur;
    cur->_col = RED;

    if (kot(parent->_data)< kot(data))
    {
    parent->_right = cur;
    }
    else
    {
    parent->_left = cur;
    }
    cur->_parent = parent;

    while (parent && parent->_col == RED)
    {
    Node* grandfather = parent->_parent;
    if (grandfather->_left == parent)
    {
    Node* uncle = grandfather->_right;
    // 叔叔存在且为红->变色
    if (uncle && uncle->_col == RED)//情况1:变? c为红,p为红,g为?,u存在且为红
    {
    // g
    // p u
    parent->_col = BLACK;
    uncle->_col = BLACK;
    grandfather->_col = RED;
    // 继续往上处理
    cur = grandfather;
    parent = cur->_parent;
    }

    else
    {
    if (cur == parent->_left) // 情况2:单旋+变? c为红,p为红,g为?,u不存在或者u存在且为?
    {
    // g
    // p u
    //c
    // 右单旋
    RotateR(grandfather);
    parent->_col = BLACK;
    grandfather->_col = RED;
    }
    else //情况3:双旋+变? c为红,p为红,g为?,u不存在或者u存在且为?,u不存在,
    {
    // g
    // p u
    // c
    // 左右单旋
    RotateL(parent);
    RotateR(grandfather);
    cur->_col = BLACK;
    grandfather->_col = RED;
    }
    break;
    }
    }
    else // grandfather->_right == parent
    {
    // g
    // u p
    Node* uncle = grandfather->_left;
    // 叔叔存在且为红,-》变色即可
    if (uncle && uncle->_col == RED)//情况1:变? c为红,p为红,g为?,u存在且为红
    {
    parent->_col = BLACK;
    uncle->_col = BLACK;
    grandfather->_col = RED;

    // 继续往上处理
    cur = grandfather;
    parent = cur->_parent;
    }
    else// 情况2:单旋+变? c为红,p为红,g为?,u不存在或者u存在且为?
    {
    // 情况二:叔叔不存在或者存在且为黑
    // 旋转+变色

    if (cur == parent->_right)
    {
    // g
    // u p
    // c
    RotateL(grandfather);
    parent->_col = BLACK;
    grandfather->_col = RED;
    } //情况3:双旋+变? c为红,p为红,g为?,u不存在或者u存在且为?,u不存在,
    else
    {// g
    // u p
    // c
    RotateR(parent);
    RotateL(grandfather);
    cur->_col = BLACK;
    grandfather->_col = RED;
    }
    break;
    }
    }
    }
    _root->_col = BLACK;
    return {Iterator(newnode), true};
    }
    private:
    Node* Find(const K& key)
    {
    KeyOfT kot;
    Node* cur = _root;
    while (cur)
    {
    if (kot(cur->_data) < key)
    {
    cur = cur->_right;
    }
    else if (kot(cur->_data) > key)
    {
    cur = cur->_left;
    }
    else
    {
    return cur;
    }
    }
    return nullptr;
    }
    void RotateR(Node* parent)
    {
    Node* subL = parent->_left;
    Node* subLR = subL->_right;

    parent->_left = subLR;
    if (subLR)
    subLR->_parent = parent;

    Node* parentParent = parent->_parent;

    subL->_right = parent;
    parent->_parent = subL;

    if (parent == _root)
    {
    _root = subL;
    subL->_parent = nullptr;
    }
    else
    {
    if (parentParent->_left == parent)
    {
    parentParent->_left = subL;
    }
    else
    {
    parentParent->_right = subL;
    }

    subL->_parent = parentParent;
    }
    }

    void RotateL(Node* parent)
    {
    Node* subR = parent->_right;
    Node* subRL = subR->_left;

    parent->_right = subRL;
    if (subRL)
    subRL->_parent = parent;

    Node* parentParent = parent->_parent;

    subR->_left = parent;
    parent->_parent = subR;

    if (parent == _root)
    {
    _root = subR;
    subR->_parent = nullptr;
    }
    else
    {
    if (parentParent->_left == parent)
    {
    parentParent->_left = subR;
    }
    else
    {
    parentParent->_right = subR;
    }

    subR->_parent = parentParent;
    }
    }
    };

    补充:STL 红黑树真正实现:用 哨兵节点 (header) 当 end ()

    两种 end () 实现对比(最清晰)

    实现方式end () 表示++ 最后走到–end()
    你的简单版 nullptr nullptr 必须特殊处理
    STL 官方版 哨兵 header 节点 header 节点 直接取 header->right

    Set和Map封装

    #include"RBTree.h"

    namespace bit
    {
    template<class K>
    class set
    {
    struct SetKeyOfT
    {
    const K& operator()(const K& key)
    {
    return key;
    }
    };
    public:
    typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
    iterator begin()
    {
    return _t.Begin();
    }

    iterator end()
    {
    return _t.End();
    }

    pair<iterator, bool> insert(const K& k)
    {
    return _t.Insert(k);
    }
    private:
    RBTree<K, const K, SetKeyOfT> _t;
    };
    };

    namespace bit
    {
    template<class K, class V>
    class map
    {
    struct MapKeyOfT
    {
    const K& operator()(const pair<K, V>& kv)
    {
    return kv.first;
    }
    };
    public:
    typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;
    iterator begin()
    {
    return _t.Begin();
    }

    iterator end()
    {
    return _t.End();
    }

    pair<iterator, bool> insert(const pair<K, V>& kv)
    {
    return _t.Insert(kv);
    }

    V& operator[](const K& key)
    {
    pair<iterator, bool> ret = insert({ key, V() });
    return ret.first->second;
    }

    private:
    RBTree<K, pair<const K, V>, MapKeyOfT> _t;
    };
    };


    赞(0)
    未经允许不得转载:171主机测评 » 【C++】 红黑树封装 STL set/map 超详细解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址