从红黑树到 map/set ——看穿 STL 的封装艺术

【STL】用一棵红黑树同时封装set和map 下面我们将对一颗kv模型的红黑树进行封装并且模拟实现出stl库当中的map和set以下是需要使用到的红黑树源代码: 红黑树模板参数的控制 我们知道set是一个k模型的容器,而map是一个kv模型的容器,那么我们如何使用一颗kv模型的黑红树来同时封装它们两个呢?我们需要控制我们传进红黑树中的模板参数,来进行对set和map的兼容为了和原红黑树的模板参数进行区分,这里我们将红黑树的第二个模板参数修改为T T可以是键值Key,也可以是键值对Key和Value,如果是set容器那么它传入底层红黑树的就是K 阅读详情

前言

上一篇文章中,我们用两个模板参数K和V实现了红黑树。但如果去翻翻 SGI-STL 的源码,会发现红黑树的模板参数长这样——rb_tree<Key, Value, KeyOfValue, Compare, Alloc>,五个参数,每个都有它的来头。为什么 set 和 map 共用同一棵红黑树?为什么 set 的 value_type 是 const K 而 map 是 pair<const K, V>?本文从 STL 源码的设计思路出发,一步步把一棵裸红黑树封装成可以直接拿来用的 set 和 map。


1. 从 STL 源码看设计思路

SGI-STL 是 Linux 下 GNU C++ 标准库的前身,map 和 set 的底层红黑树实现就是从这里来的。我们先看它怎么组织代码:

// stl_set.h —— set 的模板参数
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
    typedef Key key_type;
    typedef Key value_type;          // set 的 value 就是 key 本身
private:
    typedef rb_tree<key_type, value_type,
                    identity<value_type>,   // ← 关键:怎么从 value 里取 key
                    key_compare, Alloc> rep_type;
    rep_type t;
};
// stl_map.h —— map 的模板参数
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
    typedef Key key_type;
    typedef T mapped_type;
    typedef pair<const Key, T> value_type;  // map 的 value 是键值对
private:
    typedef rb_tree<key_type, value_type,
                    select1st<value_type>,  // ← 关键:从 pair 里取 first
                    key_compare, Alloc> rep_type;
    rep_type t;
};

两个类,共用同一套 rb_tree 模板,区别只在于传入的类型和那个提取 key 的仿函数。这个设计非常简洁,核心思想就是一句话:红黑树只负责按 key 排序,至于 key 怎么从 value 里拿出来,你传个仿函数告诉我就行。


2. 改造红黑树:从 pair<K,V> 到泛型 T

上一篇文章里我们实现的红黑树,节点存的是 pair<K, V>,比较时直接用 kv.first。这只能服务于一种场景。要让同一棵树既能存 K(set)又能存 pair<K,V>(map),第一步就是把节点的数据类型泛化。

2.1 节点结构

template<class T>
struct RBTreeNode
{
    T _data;                       // 不再写死 pair,用泛型 T
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    Colour _col;

    RBTreeNode(const T& data)
        : _data(data)
        , _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
    {}
};

2.2 红黑树模板参数

template<class K, class T, class KeyOfT>
class RBTree
{
    // K    : key 的类型,Find/Erase 的参数会用到
    // T    : 节点里存的数据类型(对于 set 是 K,对于 map 是 pair<const K, V>)
    // KeyOfT : 仿函数,从 T 中提取 K
    ...
};

三个模板参数,各司其职:

参数set 传入map 传入作用
Kintstring键的类型,Find(key) 的参数
Tconst intpair<const string, int>节点存储的数据类型
KeyOfTSetKeyOfTMapKeyOfT从 T 中提取 K 的仿函数

2.3 关键改进:用 KeyOfT 替代直接访问 .first

改造前的 Insert:

// 旧代码:写死了 pair 的 first 作为 key
if (kv.first < cur->_kv.first)  { ... }

改造后:

// 新代码:通过 KeyOfT 提取 key,不关心 T 到底是什么类型
KeyOfT kot;
if (kot(data) < kot(cur->_data))  { ... }

这一层间接性就是封装的精髓——红黑树不需要知道 T 是 pair 还是 int,它只需要知道怎么从 T 中拿到 key。增删查改的逻辑完全不用动,只换了一个取值方式。


3. 迭代器:让红黑树支持范围 for

没有迭代器的容器只能手动 Find,有了迭代器才能用 for (auto e : s) 这种写法,才算一个合格的 STL 风格容器。

3.1 迭代器的前进逻辑(operator++)

中序遍历的下一个节点怎么找?分两种情况:

情况一:当前节点有右子树
  → 下一个节点 = 右子树的最左节点

      cur
     /   \
   ...   (右子树)
          /
        (一路向左)
        /
     答案 ←

情况二:当前节点没有右子树
  → 沿着父指针向上走,直到找到一个祖先,当前节点在这个祖先的左子树中

       答案
        \
        ...
        /
      parent
        \
        cur  (cur 在 parent 的右边,继续往上)
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;
}

3.2 迭代器的后退逻辑(operator--)

operator-- 是 operator++ 的镜像,但有一步特殊处理——处理 end():

Self& operator--()
{
    if (_node == nullptr)  // 当前指向 end()
    {
        // --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;
}

这也是为什么迭代器内部要存一个 _root 指针——在处理 --end() 时需要回到树里找最大节点。

3.3 迭代器为什么要保留 _root

完整迭代器结构:

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
    typedef RBTreeNode<T> Node;
    Node* _node;   // 当前指向的节点
    Node* _root;   // 整棵树的根(operator-- 从 end() 回去时会用到)
    ...
};

_root 只有一个用途:当迭代器指向 end()(即 nullptr)时,执行 --it 需要找到整棵树的最右节点,而迭代器自身没有别的办法拿到树的根,只能靠构造时就存好。

SGI-STL 的做法更巧妙:它用一个哨兵节点 header 来同时充当 end() 和根节点的父节点,header->left 指向最左,header->right 指向最右,--end() 直接取 header->right。我们的实现简化了这个设计,直接在迭代器里记一个 _root 指针,逻辑更直观。

3.4 const 与非 const 迭代器

Ref 和 Ptr 这两个模板参数的作用就是区分 iterator 和 const_iterator:

// RBTree 中定义
typedef RBTreeIterator<T, T&, T*>           Iterator;       // Ref=T&, Ptr=T*
typedef RBTreeIterator<T, const T&, const T*> ConstIterator;  // Ref=const T&, Ptr=const T*

这样 operator* 和 operator-> 的返回值自动适配:

Ref operator*()  { return _node->_data; }   // Iterator → T&, ConstIterator → const T&
Ptr operator->() { return &_node->_data; }  // Iterator → T*, ConstIterator → const T*

4. 改造 Insert:返回 pair<iterator, bool>

STL 中 insert 的返回值是一个 pair<iterator, bool>,iterator 指向插入元素(无论成功还是失败),bool 表示是否插入成功:

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

    KeyOfT kot;
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
        if (kot(data) < kot(cur->_data))
        {
            parent = cur;
            cur = cur->_left;
        }
        else if (kot(data) > kot(cur->_data))
        {
            parent = cur;
            cur = cur->_right;
        }
        else
        {
            return { Iterator(cur, _root), false };  // key 已存在,返回已存在元素迭代器 + false
        }
    }

    cur = new Node(data);
    Node* newnode = cur;    // 保存下来,最后返回
    cur->_col = RED;
    // ... 挂载到父节点,红黑树调整(不变)...

    _root->_col = BLACK;
    return { Iterator(newnode, _root), true };  // 返回新节点迭代器 + true
}

注意 newnode 变量的作用:红黑树调整过程中 cur 可能会被改成别的节点(情况一的向上回溯),所以必须在一开始就保存新节点的指针,最后返回时用它。


5. 封装 set:key 即 value

有了迭代器和改造后的 Insert,封装 set 就很简单了——本质上就是一个只暴露了 key 相关接口的壳:

namespace jys
{
    template<class K>
    class set
    {
        struct SetKeyOfT
        {
            const K& operator()(const K& key)
            {
                return key;          // set 的 value 就是 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();
        }

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

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

值得注意的细节:RBTree 的第二个模板参数传的是 const K 而不是 K。这是因为 set 的 key 不能被修改——如果允许通过迭代器改 key,整棵树的排序就乱掉了。const K 让 Iterator 的 operator* 返回 const K&,编译期就杜绝了非法修改。


6. 封装 map:key 和 value 分离

map 的封装略复杂一些,因为存储的是键值对,并且需要支持 operator[]:

namespace jys
{
    template<class K, class V>
    class map
    {
        struct MapKeyOfT
        {
            const K& operator()(const pair<K, V>& kv)
            {
                return kv.first;     // 从 pair 中提取 key(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();
        }

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

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

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

6.1 为什么是 pair<const K, V> 而不是 pair<K, V>?

和 set 一样的原因——防止 key 被修改。map 的 key 同样是排序依据,不能动。传入 const K 作为 first 后,迭代器的 operator* 返回 pair<const K, V>&,first 不可改,second 可改:

it->first  = new_key;   // ❌ 编译错误
it->second = new_value;  // ✅ 正确

6.2 operator[] 的实现

operator[] 是 map 最常用的接口之一,它的语义是:

  • 如果 key 存在,返回对应 value 的引用
  • 如果 key 不存在,插入 {key, V()} 并返回新插入 value 的引用

这在红黑树 Insert 返回 pair<iterator, bool> 的基础上,两行就能搞定:

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

ret.first 是迭代器,指向 pair<const K, V>;ret.first->second 是 V&。无论 key 之前存不存在,这个引用都指向正确的 value。


7. 测试:验证整个体系

void test_set()
{
    jys::set<int> s;
    s.insert(1);
    s.insert(6);
    s.insert(3);
    s.insert(8);
    s.insert(9);

    jys::set<int>::iterator sit = s.begin();
    while (sit != s.end())
    {
        cout << *sit << " ";   // 输出:1 3 6 8 9(中序有序)
        ++sit;
    }
    cout << endl;
}

void test_map()
{
    jys::map<string, string> dict;
    dict.insert({ "sort", "排序" });
    dict.insert({ "left", "左边" });
    dict.insert({ "right", "右边" });

    dict["left"] = "左边(剩余)";   // 修改已存在的
    dict["insert"] = "插入";        // 插入新的(key 不存在)
    dict["string"];                 // 插入空的(value 用默认构造)

    jys::map<string, string>::iterator it = dict.begin();
    while (it != dict.end())
    {
        // it->first = "xxx";    // ❌ 编译错误,key 不可修改
        it->second += "x";       // ✅ value 可以修改
        cout << it->first << ":" << it->second << endl;
        ++it;
    }

    // 范围 for —— 同样是靠迭代器支持
    for (auto& kv : dict)
    {
        cout << kv.first << ":" << kv.second << endl;
    }
}

8. 总结

回顾整个封装过程,核心思路其实只有一条:把红黑树中"依赖具体类型"的部分抽出来,交给模板参数和仿函数去适配。

第一步,把节点数据类型从写死的 pair<K, V> 泛化为模板参数 T。这步做完,同一棵树就能同时存储 int(给 set 用)和 pair<const K, V>(给 map 用)。第二步,用 KeyOfT 仿函数替代直接访问 .first。红黑树的插入、查找逻辑只跟 key 的比较有关,至于 key 是怎么从 T 里取出来的——set 直接返回自身,map 返回 pair.first——树本身不需要关心。第三步,给红黑树配上迭代器,让 operator++ / operator-- 走中序遍历的顺序,Begin() 返回最左节点,End() 返回 nullptr 作为哨兵,operator-- 从 end() 回到最大值节点时靠迭代器里存的 _root 找到树的最右节点。这套迭代器到位后,范围 for、find、operator[] 等上层接口就能自然地搭建出来。第四步,Insert 返回值从 bool 改为 pair<iterator, bool>——这是支撑 map::operator[] 的关键,[] 本质上就是调用 insert,然后从返回的迭代器里取出 second。

经过这四步,红黑树还是那棵红黑树,旋转和变色的逻辑也无需修改,但 set 和 map 两种接口风格截然不同的容器已经架在了同一套底层代码之上。树负责按 key 排序,仿函数负责从 value 中提取 key,容器负责对外暴露接口——三者层层封装,职能明确,这就是STL封装的艺术。


如有纰漏,敬请指正

【C++】详解STL之set和map --- 关联式容器的再封装 SGI-STL30版本源代码,map和set等几个头文件中。•通过下图对框架的分析,我们可以看到现key的搜索场景,还是key/value的搜索场景不是直接写死的,而是由第二个模板参数Value决定_rb_tree_node中存储的数据类型。•set实例化rb_tree时第而个模板参数给的是key,map实例化rb_tree时第二个模板参数给的是这样一颗红黑树既可以实现key搜索场景的set,也可以实key/value搜索场景的map。•要注意一下, 阅读详情

相关推荐

一文说清STM32CubeMX中文汉化全流程(图解说明)

详细讲解STM32CubeMX中文汉化的完整流程,通过图文并茂的方式手把手教学,解决界面英文带来的使用障碍,让开发更高效。掌握stm32cubemx中文汉化技巧,提升嵌入式开发体验。

weixin_42499004的博客 6254

【C++/STL】map和set的封装(红黑树)

💬 hello!各位铁子们大家好哇。今日更新了map和set封装的相关内容。

qinjh_的博客 3107

别再手动调参了!用MATLAB+改进PSO算法,5步搞定机械臂轨迹优化(附完整代码)

本文介绍如何利用MATLAB和改进PSO算法快速优化机械臂轨迹规划,仅需5步即可完成传统耗时的手动调参过程。通过混沌初始化、自适应权重等创新方法,改进PSO算法显著提升优化效率,实现全局最优解搜索,适用于工业自动化中的机械臂控制。文章包含完整MATLAB代码实现,帮助工程师轻松应用于实际生产场景。

weixin_29232121的博客 352

STL:map和set的封装

利用红黑树模拟实现STL中的map和set!!!!

weixin_51142926的博客 2700

告别浏览器崩溃!EasyPlayer.js长时间播放FLV视频内存泄漏排查指南

本文针对EasyPlayer.js长时间播放FLV视频导致浏览器内存泄漏和崩溃的问题,提供了一套系统性排查与解决方案。核心策略包括将视频编码从H.265转为H.264以降低解码压力,以及将传输协议从HTTP-FLV切换至WebRTC以优化资源管理。文章还详细介绍了播放器实例生命周期管理、浏览器开发者工具监控等精细化排查手段,帮助开发者实现稳定、长时间的多路视频播放。

weixin_29229055的博客 170

基于改进YOLO11的天际线检测算法:复杂光照与恶劣天气适配实战

针对特定任务的YOLO改进,不应追求通用SOTA,而应深入理解任务的物理退化机理。Retinex对应光照模型、FAD对应散射模型、Wise-IoU对应边界不确定性——每个改进都有明确的物理解释,而非盲目的模块堆叠。时序一致性:引入轻量级RNN/Transformer模块利用视频帧间信息补偿单帧退化;多模态融合:结合红外/NIR波段穿透雾霾,与可见光特征在FAD-Neck中交叉注意力融合;自监督预训练:在海量无标注天际线视频上用MAE预训练Backbone,缓解恶劣天气标注稀缺问题。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 93

C++ 改造红黑树,封装map和set

C++ 改造红黑树,封装map和set

Wzs040810的博客 1757

《八》:玩家位置同步(玩家防碰撞)

在VR大空间体验中,由于玩家佩戴头显设备无法直接观察彼此位置,容易发生碰撞。因此,需要将玩家的实时位置信息绘制在虚拟空间中,实现位置可视化。通过获取头显的位置和旋转角度等数据,可以推算出玩家的移动状态和位置。使用可序列化的玩家结构体,通过UDP将玩家数据发送到服务器,服务器接收并反序列化数据后,同步所有玩家的最新信息至各客户端。客户端根据接收到的数据渲染虚拟人物模型,实现动态位置更新。这一过程涉及数据序列化、反序列化、网络通信和实时渲染等技术,确保玩家在虚拟空间中的位置同步和可视化。

unitygroup的博客 412

C++:用红黑树封装map与set-1

从STL源码出发,深度解析了set和map基于红黑树的实现原理及其封装过程。通过分析红黑树的数据结构、插入逻辑,以及set和map节点数据存储差异,采用仿函数解决了键值提取和比较问题。

Jdxxwu的博客 4976

从零开始:C#上位机集成YOLO完成摄像头目标追踪(保姆级教程)

新手入门C#上位机+YOLO做摄像头目标追踪,最忌讳的是“只抄代码,不懂逻辑”。这个教程不仅给了完整代码,还把核心原理和踩坑点都讲清楚了,新手跟着走,不仅能跑通demo,还能理解为什么这么写。如果你们在运行时遇到具体问题,比如模型导出失败、代码报错,都可以在评论区留言,我会一一解答。后续也可以出一期工业相机+YOLO目标追踪的教程,帮助大家把demo落地到实际生产中。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1025

VTK-9.5.0 1D2D3DWidget实现灰度图+深度图+3D点云显示(可轻松集成到自己的项目)

VTK1D2D3DWidget是一个基于Qt和VTK开发的3D点云可视化工具,支持RGB和深度点云数据的显示与处理。主要功能包括点云加载、可视化、保存为PLY格式文件,以及背景颜色、点间隔、坐标轴显示等参数的自定义设置。该工具采用模块化设计,核心组件包括主窗口管理类、VTK渲染组件和自定义鼠标交互样式,依赖Qt5+和VTK9.5.0库实现跨平台支持。项目提供完整的源码和构建指南,适用于点云数据的快速展示与分析。

Qt历险记,专注Qt技术,三年医疗设备研发经验,1年机器视觉研发经验,半年柔性传感研发经验。 385

【C++/STL】:红黑树的应用 --- 封装map和set

map 和 set 的底层本质上还是复用通过对红黑树的改造,再分别套上一层 map 和 set 的 “壳子”,以达到 “一树二用” 的目的。在改造红黑树的过程中,我大概归纳了以下几个需要重点解决的问题:(1) 对红黑树节点的改造。关联式容器中存储的是的键值对,K为key的类型,如果是 set 则是 K,如果是map,则为pair。如何用一个节点结构控制两种类型,使用类模板参数T。(2) 在插入操作时,如何完成数据的比较。

2301_77900444的博客 2362

[解决] PDF转图片,中文乱码或显示方框的解决方案

在Java开发中,将PDF文件转换为图片是一项常见的需求,但过程中可能会遇到中文乱码或显示方框的问题。本文将深入探讨这一问题,并提供详细的解决方案,帮助开发者顺利地完成PDF到图片的转换。

DazedMen的博客 1922

【STL库】map/set 的封装原理

本文介绍了如何利用红黑树封装实现STL中的map和set容器。通过源码分析,作者发现STL开发者采用了巧妙的泛型设计,使红黑树能够同时支持key和key/value两种搜索场景。具体实现中,map传入pair<lt;const key, T>;作为模板参数,set则直接传入key。文章详细讲解了红黑树框架的构建,包括结点结构、插入操作以及迭代器实现的关键技术点。特别说明了插入操作中比较键值时的仿函数处理方式,以及如何通过模板参数控制红黑树存储的数据类型。最后简要提及了迭代器的实现思路。

Linux、C++、图形软件开发等。不一定每次都能写出完美的代码,但会认真搞懂每一个问题。 1296

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

【C++学习手札】基于红黑树封装模拟实现map和set

本文是作者学习map和set时对于map和set知识点的总结以及模拟实现,可能还不够全面,但是作者已经尽力了,毕竟跟上上一篇、上一篇一样超万字了O(≧口≦)O

B1ue还在专注 3654

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

手把手实现 STL Set/Map:从零编写一棵红黑树到完整容器封装

用过 STL 的开发者都知道,map 和 set 是高频使用的关联式容器,其底层都依赖红黑树实现 —— 但你知道红黑树如何通过泛型设计同时支撑 “key-only”(set)和 “key-value”(map)两种场景吗?为什么 set 的迭代器不可修改,而 map 只能修改 value 不能修改 key?本文结合 源码框架与实现细节,从红黑树的泛型改造入手,一步步拆解 myMap 和 mySet 的封装逻辑,包括关键的仿函数设计、迭代器实现、key 不可修改约束及 map 的 [] 运算符重载,全程附可运

会当凌绝顶,一览众山小 1918

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

【C++】基于红黑树的 Map 和 Set 封装及实现过程详述

大家好,我是店小二。今天我将为大家详细讲解如何基于红黑树封装和实现 Map 和 Set,并深入解析底层逻辑。希望通过这篇文章,能帮助大家在使用 Map 和 Set 类相关接口时更加得心应手。

专注于 C/C++ 后端开发,涵盖基础语法、Linux系统与网络、MySQL数据库、常见算法及项目经验。 2962

封装红黑树实现map和set

之前我们学习了set与map容器的如何使用,红黑树的实现。接下来我们来看看如何通过封装红黑树,实现我们自己的set与map。

记录学习与成长 997

【C++】Set/Map使用红黑树封装

stl库中通过这种巧妙的方法使我们只需要我们实现一棵红黑树就可以复用形成set/map的底层,通过模版的形式让编译器去帮我们生成对应的代码,泛型思维。由以下代码我们可以分析出stl库中的set和map底层都是通过红黑树所实现的。

2301_81577798的博客 1万+

【C++深度探索】红黑树实现Set与Map的封装

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2Nlog_2 Nlog2​N),但是红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对AVL树而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。今天我们就可以利用之前实现过的红黑树来对C++STL库中的set和map进行模拟实现。

Renswc的博客 2190

【C++】论如何封装红黑树模拟实现set和map

本文基于红黑树实现STL中的set和map容器。首先分析STL源码,发现set和map通过红黑树实现,其中set存储key类型,map存储pair<key,value>类型。在红黑树实现中引入Key_Of_T仿函数解决不同类型数据比较问题:set直接返回key,map返回pair的first成员。文章详细展示了红黑树节点的定义、插入操作及旋转平衡处理。通过模板参数K、T和Key_Of_T实现泛型编程,使同一红黑树能同时支持set和map的存储需求,为后续实现迭代器和map的[]操作符重载奠定基础

记录我一步步的学习历程,期待无限进步,向星举目! 2144

STL详解(十一)—— 用一棵红黑树同时封装出map和set

文章目录 用一棵红黑树同时封装出map和set

2021dragon的博客 5305

【C++历练之路】红黑树——map与set的封装实现

RED,BLACK, _kv(kv),_col(RED){}在节点定义时我们默认将节点设置成红色节点,这样如果出现连续的红色节点时,我们可以进行变色操作,通过维护一个子树来使红黑树合法,但是如果插入一个黑色节点时,我们就无法下手,因为每条路的黑色节点数必须相同,这样我们无法很好的进行操作使其合法化。

why 1600

C++ STL之封装红黑树实现map/set

本文通过分析STL源码,深入解析了红黑树封装map/set的实现机制。首先介绍了源码中map和set头文件的结构,重点解析了红黑树模板参数的设计,特别是KeyOfValue参数的作用。接着详细说明了红黑树结点的修改方案,包括使用模板参数T代替具体类型以支持泛型编程。文章还深入探讨了红黑树迭代器的实现,特别是operator++和operator--的中序遍历逻辑。最后给出了完整的模拟实现代码,包括RBTree.h、my_set.h和my_map.h三个文件,展示了如何通过一棵红黑树同时支持set和map的功

2301_79976271的博客 1112

『 C++ - STL』map与set的封装 ( 万字 )

map与set分别是STL中的两种序列式容器;它们是一种树形数据结构的容器,且其的底层构造为一棵红黑树;而在上一篇文章中提到,其实红黑树本身就是一棵二叉搜索树,是基于二叉搜索树的性质对其增加了平衡的属性来提高其综合性能(包括增删查改);当然也提到了红黑树与AVL树的区别:AVL树AVL树是一棵高度平衡搜索二叉树,其特点即为在搜索二叉树的基础上根据控制结构达到了最终的属性;即为,每一个节点的左右子树的高度差不超过1;红黑树红黑树是一棵近似平衡的二叉树,其特点为根据树的颜色(红色或是黑色)

小侯宝的博客 1375

STL——红黑树的封装及map/set的模拟实现

前面的文章当中,我们介绍了红黑树的模拟实现,我们了解到红黑树是一颗不完全平衡二叉搜索树,我们之所以要学习红黑树,不仅仅是学习它实现的思维逻辑,更多的是通过红黑树去学习map和set的底层,前面我们介绍map/set的使用中我们提到,他们的底层是用红黑树去封装实现的,那么具体是怎么完成的呢?我们将在这一篇文章当中介绍。

f040731_007的博客 1077
上一篇: 红黑树原理与C++实现详解
下一篇: 哈希表原理与开放定址法C++实现
jinyishu_
博客等级 码龄1年 53粉丝 82原创
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值