C++进阶:红黑树封装map/set & 哈希表unordered系列容器详解

本文基于SGI-STL源码思想,完整实现红黑树封装map/set,并对比讲解unordered_map/unordered_set使用与底层差异,代码可直接编译运行,适合C++后端/STL源码学习。


一、前言:STL关联式容器底层

C++ STL 关联式容器分为两大派系:

  1. 红黑树派系map/set/multimap/multiset —— 有序、去重、O(logN)
  2. 哈希表派系unordered_map/unordered_set —— 无序、去重、平均O(1)

本文先从红黑树封装map/set讲起,再对比哈希表unordered系列,带你吃透底层设计。


二、红黑树封装 map / set 核心思想

SGI-STL 中 map/set 底层共用一棵红黑树,通过泛型+仿函数解耦,实现一套红黑树支撑两种容器:

  • set:只存 key → 红黑树结点存 Key
  • map:存 key-value → 红黑树结点存 pair<const Key, Value>
  • 比较时只比 key,通过仿函数从结点数据中提取 key

2.1 关键设计点

  1. 红黑树模板参数
template<class K, class T, class KeyOfT>
class RBTree;
  • K:查找用的 key 类型
  • T:结点实际存储数据(set=K,map=pair<const K,V>)
  • KeyOfT:仿函数,从 T 中提取 key 用于比较
  1. set 封装:提取自身作为 key
  2. map 封装:提取 pair.first 作为 key
  3. 迭代器:中序遍历,模拟指针行为
  4. map operator[]:插入+返回 value 引用

三、完整代码实现(可直接运行)

3.1 红黑树结点与颜色定义

#pragma once
#include <iostream>
#include <utility>
using namespace std;

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)
        , _col(RED) // 新结点默认红色
    {}
};

3.2 红黑树迭代器实现

// 红黑树迭代器
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
    typedef RBTreeNode<T> Node;
    typedef RBTreeIterator<T, Ref, Ptr> Self;

    Node* _node;
    Node* _root;

    RBTreeIterator(Node* node, Node* root)
        :_node(node)
        ,_root(root)
    {}

    // ++:中序下一个
    Self& operator++()
    {
        if (_node->_right)
        {
            // 右子树最左结点
            Node* leftMost = _node->_right;
            while (leftMost->_left)
                leftMost = leftMost->_left;

            _node = leftMost;
        }
        else
        {
            // 向上找:孩子是父亲左的祖先
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_right)
            {
                cur = parent;
                parent = cur->_parent;
            }

            _node = parent;
        }
        return *this;
    }

    // --:中序上一个
    Self& operator--()
    {
        if (_node == nullptr) // --end()
        {
            Node* rightMost = _root;
            while (rightMost && rightMost->_right)
                rightMost = rightMost->_right;

            _node = rightMost;
        }
        else if (_node->_left)
        {
            // 左子树最右结点
            Node* rightMost = _node->_left;
            while (rightMost->_right)
                rightMost = rightMost->_right;

            _node = rightMost;
        }
        else
        {
            // 向上找:孩子是父亲右的祖先
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_left)
            {
                cur = parent;
                parent = cur->_parent;
            }

            _node = parent;
        }
        return *this;
    }

    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; }
};

3.3 红黑树主体(插入+旋转+平衡)

template<class K, class T, class KeyOfT>
class RBTree
{
    typedef RBTreeNode<T> Node;
public:
    typedef RBTreeIterator<T, T&, T*> Iterator;
    typedef RBTreeIterator<T, const T&, const T*> ConstIterator;

    Iterator Begin()
    {
        Node* leftMost = _root;
        while (leftMost && leftMost->_left)
            leftMost = leftMost->_left;
        return Iterator(leftMost, _root);
    }

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

    ConstIterator Begin() const
    {
        Node* leftMost = _root;
        while (leftMost && leftMost->_left)
            leftMost = leftMost->_left;
        return ConstIterator(leftMost, _root);
    }

    ConstIterator End() const { return ConstIterator(nullptr, _root); }

    // 插入:返回<迭代器, 是否插入成功>
    pair<Iterator, bool> Insert(const T& data)
    {
        if (_root == nullptr)
        {
            _root = new Node(data);
            _root->_col = BLACK;
            return make_pair(Iterator(_root, _root), true);
        }

        KeyOfT kot;
        Node* parent = nullptr;
        Node* cur = _root;

        // 1. 搜索插入位置
        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 make_pair(Iterator(cur, _root), false);
        }

        // 2. 插入新结点
        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;

        // 3. 平衡调整
        while (parent && parent->_col == RED)
        {
            Node* grandfather = parent->_parent;
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;
                // 情况1:叔叔存在且为红 → 变色
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else // 情况2/3:叔叔黑或不存在 → 旋转
                {
                    if (cur == parent->_left)
                    {
                        RotateR(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        RotateL(parent);
                        RotateR(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;
                }
            }
            else // 对称:parent在右
            {
                Node* uncle = grandfather->_left;
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    if (cur == parent->_right)
                    {
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        RotateR(parent);
                        RotateL(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;
                }
            }
        }
        _root->_col = BLACK;
        return make_pair(Iterator(newNode, _root), true);
    }

    // 查找
    Iterator Find(const K& key)
    {
        Node* cur = _root;
        KeyOfT kot;
        while (cur)
        {
            if (kot(cur->_data) < key)      cur = cur->_right;
            else if (kot(cur->_data) > key) cur = cur->_left;
            else                            return Iterator(cur, _root);
        }
        return End();
    }

private:
    // 左单旋
    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;

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

        Node* pp = parent->_parent;
        subR->_left = parent;
        parent->_parent = subR;

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

    // 右单旋
    void RotateR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;

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

        Node* pp = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;

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

    void Destroy(Node* root)
    {
        if (root == nullptr) return;
        Destroy(root->_left);
        Destroy(root->_right);
        delete root;
    }

private:
    Node* _root = nullptr;
};

3.4 封装 set

#pragma once
#include "RBTree.h"

namespace bit
{
    template<class K>
    class set
    {
        // 仿函数:从key提取key
        struct SetKeyOfT
        {
            const K& operator()(const K& key) { return key; }
        };

    public:
        typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
        typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;

        iterator begin() { return _t.Begin(); }
        iterator end() { return _t.End(); }
        const_iterator begin() const { return _t.Begin(); }
        const_iterator end() const { return _t.End(); }

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

        iterator find(const K& key)
        {
            return _t.Find(key);
        }

    private:
        RBTree<K, const K, SetKeyOfT> _t;
    };
}

3.5 封装 map(含 operator[])

#pragma once
#include "RBTree.h"

namespace bit
{
    template<class K, class V>
    class map
    {
        // 仿函数:从pair提取key
        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;
        typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;

        iterator begin() { return _t.Begin(); }
        iterator end() { return _t.End(); }
        const_iterator begin() const { return _t.Begin(); }
        const_iterator end() const { return _t.End(); }

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

        iterator find(const K& key)
        {
            return _t.Find(key);
        }

        // map核心:operator[]
        V& operator[](sslocal://flow/file_open?url=const+K%26+key&flow_extra=eyJsaW5rX3R5cGUiOiJjb2RlX2ludGVycHJldGVyIn0=)
        {
            pair<iterator, bool> ret = insert(make_pair(key, V()));
            return ret.first->second;
        }

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

3.6 测试代码

#include "MySet.h"
#include "MyMap.h"

void test_set()
{
    bit::set<int> s;
    int arr[] = { 4,2,6,1,3,5,15,7,16,14 };
    for (auto e : arr) s.insert(e);

    // 遍历有序
    for (auto e : s) cout << e << " ";
    cout << endl;
}

void test_map()
{
    bit::map<string, string> dict;
    dict.insert({ "sort","排序" });
    dict.insert({ "left","左边" });
    dict["right"] = "右边";
    dict["insert"] = "插入";

    for (auto& kv : dict)
        cout << kv.first << " : " << kv.second << endl;
}

int main()
{
    test_set();
    test_map();
    return 0;
}

四、unordered_map / unordered_set 使用与对比

4.1 底层结构

  • unordered_map/unordered_set哈希桶(拉链法)
  • 平均 O(1),最坏 O(N)(哈希冲突严重)
  • 遍历无序

4.2 与红黑树容器核心差异

特性map/setunordered_map/unordered_set
底层红黑树哈希表(拉链法)
顺序有序(中序)无序
查找效率O(logN)平均O(1)
key要求支持 < 比较支持哈希、== 比较
迭代器类型双向迭代器单向迭代器
内存占用较低较高(哈希桶空间)

4.3 使用示例

#include <unordered_map>
#include <unordered_set>
#include <iostream>
using namespace std;

void test_unordered()
{
    unordered_set<int> us;
    us.insert(1); us.insert(3); us.insert(2);
    for (auto e : us) cout << e << " "; // 无序

    unordered_map<string, int> um;
    um["a"] = 1; um["b"] = 2;
    cout << um["a"] << endl;
}

五、总结

  1. map/set 复用红黑树:通过泛型+仿函数提取 key,一套代码支撑两种容器。
  2. 迭代器本质:中序遍历,模拟指针,++/-- 严格遵循中序规则。
  3. map operator[]insert + 返回 value 引用,是日常高频用法。
  4. unordered 系列:哈希表实现,无序、更快,适合纯查找场景。

更多推荐