从零开始 C++----- 十二【C++ 数据结构】map/set 全解析:从使用到红黑树底层模拟实现

系列文章目录

提示:这里是系列文章的专栏

并不喜欢吃鱼的C++专栏


提示:以下是文章目录哦!

文章目录

目录

​编辑

系列文章目录

文章目录

前言

一、序列式容器与关联式容器

[1.1 两类容器概念区分](#1.1 两类容器概念区分)

序列式容器

关联式容器

[1.2 核心差异对比](#1.2 核心差异对比)

[1.3 map 与 set 底层本质](#1.3 map 与 set 底层本质)

[二、set 系列容器的使用](#二、set 系列容器的使用)

[2.1 set 和 multiset 参考文档](#2.1 set 和 multiset 参考文档)

[2.2 set 类核心介绍](#2.2 set 类核心介绍)

[2.2.1 模板参数](#2.2.1 模板参数)

[2.3 set 的构造与迭代器](#2.3 set 的构造与迭代器)

[2.3.1 常用构造方式](#2.3.1 常用构造方式)

[2.3.2 迭代器使用](#2.3.2 迭代器使用)

[2.4 set 的增删查核心接口](#2.4 set 的增删查核心接口)

[2.4.1 常用接口总览](#2.4.1 常用接口总览)

[2.5 insert 和迭代器遍历实战示例](#2.5 insert 和迭代器遍历实战示例)

[2.6 find 和 erase 实战示例](#2.6 find 和 erase 实战示例)

[2.7 lower_bound 与 upper_bound 区间删除](#2.7 lower_bound 与 upper_bound 区间删除)

[2.8 multiset 和 set 的核心差异](#2.8 multiset 和 set 的核心差异)

[三、map 系列容器的使用](#三、map 系列容器的使用)

[3.1 map 和 multimap 参考文档](#3.1 map 和 multimap 参考文档)

[3.2 map 类核心介绍](#3.2 map 类核心介绍)

模板参数

核心特性

[3.3 pair 类型详解](#3.3 pair 类型详解)

底层源码简化

使用方式

[3.4 map 的构造](#3.4 map 的构造)

[3.5 map 的增删查核心接口](#3.5 map 的增删查核心接口)

[3.6 map 的数据修改规则](#3.6 map 的数据修改规则)

[3.7 map 四种插入方式 + 遍历示例](#3.7 map 四种插入方式 + 遍历示例)

[3.8 map \[\] 运算符底层原理与使用](#3.8 map [] 运算符底层原理与使用)

底层实现逻辑

经典场景:统计元素出现次数

[3.9 multimap 和 map 的核心差异](#3.9 multimap 和 map 的核心差异)

[四、封装红黑树实现 mymap 与 myset](#四、封装红黑树实现 mymap 与 myset)

[4.1 STL 源码框架分析](#4.1 STL 源码框架分析)

[4.2 前置准备:红黑树基础定义](#4.2 前置准备:红黑树基础定义)

红黑树节点结构

[4.3 迭代器模拟实现](#4.3 迭代器模拟实现)

一、核心思路

二、operator++(中序下一个节点)的核心逻辑

三、operator--(中序前一个节点)的核心逻辑

[四、end() 的实现方式](#四、end() 的实现方式)

[4.4 通用红黑树封装](#4.4 通用红黑树封装)

[4.5 模拟实现 myset](#4.5 模拟实现 myset)

[4.6 模拟实现 mymap](#4.6 模拟实现 mymap)

五、总结与对比

[5.1 set/multiset 对比](#5.1 set/multiset 对比)

[5.2 map/multimap 对比](#5.2 map/multimap 对比)

[5.3 底层核心设计回顾](#5.3 底层核心设计回顾)

[5.4 适用场景选型](#5.4 适用场景选型)



前言

提示:这里可以添加本文要记录的大概内容:

在前面的文章中,我们已经系统学习了二叉搜索树(BST)、AVL 树与红黑树这三种自平衡二叉搜索树的原理与实现。而在 C++ 的 STL 标准库中,map和set正是以红黑树为底层结构封装的关联式容器,它们是实际开发中解决高效查找、有序管理等问题的核心工具。
本文将从实际使用出发,完整讲解set/multiset与map/multimap的接口用法、特性差异与典型场景,并最终带你基于红黑树框架,从零模拟实现简化版的mymap与myset,打通从 "API 调用" 到 "底层原理" 的完整链路,真正理解 STL 关联式容器的设计思想。


提示:以下是本篇文章正文内容

一、序列式容器与关联式容器

1.1 两类容器概念区分

序列式容器

代表容器:stringvectorlistdequearray

  • 逻辑结构:线性结构,元素按存储位置排列
  • 元素关系:元素之间无关键字关联,交换位置不破坏容器结构
  • 访问方式:按位置访问,支持随机 / 顺序遍历

关联式容器

代表容器:set/multisetmap/multimapunordered_set/unordered_map

  • 逻辑结构:非线性树形结构(红黑树 / 哈希表)
  • 元素关系:元素依靠关键字 key关联,交换元素会破坏底层结构
  • 访问方式:按关键字查找访问,自动排序

1.2 核心差异对比

| 特性 | 序列式容器 | 关联式容器(map/set) |
| 底层结构 | 线性数组 / 链表 | 红黑树(平衡二叉搜索树) |
| 查找效率 | O (N) 逐个遍历 | O (logN) 二分查找 |
| 元素顺序 | 由插入 / 存储位置决定 | 由 key 大小自动升序排序 |

核心用途 顺序存储、随机访问、尾部增删 快速查找、去重、键值映射、有序管理

1.3 map 与 set 底层本质

  • set:纯 key 模型,只存储关键字,自动去重 + 排序
  • map:key-value 键值对模型,key 唯一,映射对应 value
  • 两者底层复用同一套红黑树,仅存储数据类型不同

下面我们来直观感受一下:

cpp 复制代码
#include <iostream>   // 标准输入输出头文件,用于cout、cin
#include <set>      // STL set容器头文件
#include <map>      // STL map容器头文件
#include <typeinfo> // 类型信息头文件,用于获取变量/对象的类型(typeid使用)
using namespace std;

int main()
{
    // ------------------------------
    // 1. 验证 set 的纯 key 模型
    // set特点:元素唯一、自动排序、底层红黑树、元素只读不可修改
    // ------------------------------
    cout << "===== set 纯 key 模型验证 =====" << endl;
    // 初始化set,自动去重+升序排序,重复的1会被过滤
    set<int> s = {3, 1, 4, 1, 5, 9, 2, 6}; 
    cout << "set 元素遍历(自动升序、无重复):" << endl;
    // 范围for遍历set,输出中序遍历结果(有序)
    for (auto e : s)
    {
        cout << e << " ";
    }
    // 输出set有效元素个数
    cout << "\nset 实际元素个数:" << s.size() << endl;

    // 验证 set 元素不可修改(解开注释会编译报错)
    // 原因:set元素底层被const修饰,修改会破坏红黑树结构
    // *s.begin() = 100; 

    // typeid获取迭代器解引用后的元素类型,name()输出类型名字
    // 用于证明:set的元素是const类型,只读不可修改
    cout << "set 元素类型:" << typeid(*s.begin()).name() << endl;

    // ------------------------------
    // 2. 验证 map 的 key-value 键值对模型
    // map特点:key唯一、按key排序、key不可改、value可改、支持[]运算符
    // ------------------------------
    cout << "\n===== map 键值对模型验证 =====" << endl;
    // 定义map:key是string(水果名),value是int(数量)
    map<string, int> cnt;
    // []运算符:key不存在则插入默认值0,再++;存在则直接++
    cnt["苹果"]++;
    cnt["西瓜"]++;
    cnt["苹果"]++;
    // 直接赋值:key不存在则插入,存在则修改value
    cnt["香蕉"] = 10;

    cout << "map 键值对遍历(key升序,key唯一):" << endl;
    // 遍历map,kv是pair对象,kv.first=key,kv.second=value
    for (auto& kv : cnt)
    {
        cout << kv.first << " : " << kv.second << endl;
    }

    // find查找key为"苹果"的节点,返回对应迭代器
    auto it = cnt.find("苹果");
    // 迭代器不等于end(),说明找到
    if (it != cnt.end())
    {
        // it->first是key,被const修饰,不能修改(解开注释编译报错)
        // it->first = "桃子"; 
        
        // it->second是value,可以任意修改
        it->second = 999;     
        cout << "修改后苹果的 value:" << it->second << endl;
    }

    // ------------------------------
    // 3. 验证底层红黑树特性:高效查找 O(logN)
    // set/map的find是红黑树二分查找,远快于线性遍历
    // ------------------------------
    cout << "\n===== 红黑树底层特性验证 =====" << endl;
    
    // 在set中查找元素5,找到返回对应迭代器
    if (s.find(5) != s.end())
        cout << "set 中找到元素 5(红黑树 O(logN) 查找)" << endl;
    
    // 在map中查找key为"香蕉"的节点
    if (cnt.find("香蕉") != cnt.end())
        cout << "map 中找到 key 为香蕉的节点(红黑树 O(logN) 查找)" << endl;

    return 0;
}

运行结果如下:


二、set 系列容器的使用

2.1 set 和 multiset 参考文档

官方文档:https://legacy.cplusplus.com/reference/set/


2.2 set 类核心介绍

2.2.1 模板参数

  • T:存储的关键字类型
  • Compare:比较规则,默认less<T>升序,可自定义仿函数改为降序
  • Alloc:空间配置器,默认即可,一般无需手动传参

核心特性

  1. 元素唯一不重复,插入重复值会自动失效
  2. 底层红黑树,增删查效率 O(logN)
  3. 迭代器遍历为中序遍历,默认按 key 升序输出
  4. 迭代器只读,不允许修改元素(修改会破坏二叉搜索树结构)

2.3 set 的构造与迭代器

2.3.1 常用构造方式

这里的例子讲的很清楚,大家看一眼就可以明白

2.3.2 迭代器使用

set 支持正向迭代器、反向迭代器、const 迭代器 ,属于双向迭代器,只支持 ++/--,不支持随机访问


2.4 set 的增删查核心接口

2.4.1 常用接口总览

2.5 insert 和迭代器遍历实战示例

我们直接看例子:

cpp 复制代码
#include <iostream>
#include <set>
#include <string>
using namespace std;

int main()
{
    // 1. 默认升序插入、去重
    set<int> s;
    s.insert(5);
    s.insert(2);
    s.insert(7);
    s.insert(5); // 重复插入,无效

    // 迭代器遍历
    auto it = s.begin();
    while (it != s.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;

    // 2. 初始化列表批量插入
    s.insert({2,8,3,9});
    for (auto e : s)
    {
        cout << e << " ";
    }
    cout << endl;

    // 3. string类型按ASCII码排序
    set<string> strset = {"sort", "insert", "add"};
    for (auto& e : strset)
    {
        cout << e << " ";
    }

    return 0;
}

运行结果如下:


2.6 find 和 erase 实战示例

我们直接来看例子:

cpp 复制代码
#include <iostream>
#include <set>
#include <algorithm> // 算法库find
using namespace std;

int main()
{
    set<int> s = {4,2,7,2,8,5,9};
    for (auto e : s) cout << e << " ";
    cout << endl;

    // 1. 删除最小值(迭代器删除)
    s.erase(s.begin());
    for (auto e : s) cout << e << " ";
    cout << endl;

    // 2. 按值删除
    int x = 7;
    int num = s.erase(x);
    if (num == 0)
        cout << x << " 不存在!" << endl;
    else
        cout << "删除成功" << endl;

    // 3. 先查找再删除(推荐)
    cin >> x;
    auto pos = s.find(x);
    if (pos != s.end())
        s.erase(pos);
    else
        cout << x << " 不存在!" << endl;

    // 4. set自带find O(logN)  VS  算法库find O(N)
    auto pos1 = find(s.begin(), s.end(), x); // 遍历查找
    auto pos2 = s.find(x);                  // 红黑树高效查找

    // 5. count间接判断元素是否存在
    if (s.count(x))
        cout << x << " 存在!" << endl;
    else
        cout << x << " 不存在!" << endl;

    return 0;
}

结果如下:


2.7 lower_bound 与 upper_bound 区间删除

这里一定要注意一下这两个函数的范围

我们直接来看例子:

运行结果如下:


2.8 multiset 和 set 的核心差异

  1. set:元素唯一,不允许重复
  2. multiset:允许元素冗余重复,仅排序不去重
  3. find:multiset 返回中序第一个匹配元素
  4. count:multiset 返回元素实际个数
  5. erase:按值删除时,multiset 会删除所有匹配元素

我们直接来看例子:

运行结果如下:


三、map 系列容器的使用

3.1 map 和 multimap 参考文档

官方文档:https://legacy.cplusplus.com/reference/map/

3.2 map 类核心介绍

模板参数

  • Key:关键字类型,唯一不可重复
  • T:映射的 value 数据类型
  • Compare:key 的比较规则,默认升序
  • 底层存储:pair<const Key, T> 键值对

核心特性

  1. 存储key-value 键值对,key 唯一,value 可重复
  2. 底层红黑树,按key 自动升序排序
  3. 可修改 value,禁止修改 key(破坏树结构)
  4. 增删查效率稳定 O(logN)

3.3 pair 类型详解

map 底层节点依赖 pair 存储键值对,是 C++ 内置键值对结构体(这个我们之前讲过,所以这里不详细展开来讲解)

底层源码简化

使用方式

这里提一嘴:

make_pair 就是用来快速创建一个 pair 对象的工具函数

可以理解成:

不使用 make_pair(麻烦写法)

必须写清楚类型:

使用 make_pair(懒人写法)

不用写类型,自动推导


3.4 map 的构造

和set一样都是这四种构造方式,一定要牢记


3.5 map 的增删查核心接口

接口与 set 高度相似,仅插入为键值对,查找删除只需要传 key

这里强调一下,免得下面3.7插入看不懂

1. map 的本质:存的就是 pair

std::map<Key, Value>每个元素的类型是:

  • .first → 键(const,不能改)
  • .second → 值

2. insert 的参数类型就是 value_type(即 pair)

map 里有个别名:

而 insert 的核心重载是:

因此insert可以接受pair类型的数据


3.6 map 的数据修改规则

  1. 不可修改 key:key 是 const 修饰,修改会编译报错且破坏红黑树结构
  2. 可修改 value:通过迭代器 /\[\] 运算符修改
  3. insert 返回值:pair<iterator, bool>
    • bool:true = 插入成功,false=key 已存在
    • iterator:指向当前 key 所在节点迭代器

3.7 map 四种插入方式 + 遍历示例

cpp 复制代码
#include <iostream>
#include <map>
#include <string>
using namespace std;

int main()
{
    map<string, string> dict;

    // 四种插入方式
    // 方式1:创建pair对象插入
    pair<string, string> kv1("first", "第一个");
    dict.insert(kv1);

    // 方式2:匿名pair对象插入
    dict.insert(pair<string, string>("second", "第二个"));

    // 方式3:make_pair插入
    dict.insert(make_pair("sort", "排序"));

    // 方式4:C++11列表初始化(最常用)
    dict.insert({"auto", "自动的"});

    // 迭代器遍历
    map<string, string>::iterator it = dict.begin();
    while (it != dict.end())
    {
        // it->first 访问key,it->second访问value
        cout << it->first << " : " << it->second << endl;
        ++it;
    }
    cout << endl;

    // 范围for遍历
    for (const auto& e : dict)
    {
        cout << e.first << " : " << e.second << endl;
    }

    return 0;
}

运行结果如下:


3.8 map \[\] 运算符底层原理与使用

底层实现逻辑

  • key 不存在:插入默认值,返回 value 引用,可赋值修改
  • key 已存在:查找并返回已有 value 引用

我们直接来看例子:

经典场景:统计元素出现次数

运行结果如下:


3.9 multimap 和 map 的核心差异

  1. map:key唯一 ,支持[]运算符
  2. multimap:允许 key 重复 ,不支持[](无法确定哪个 value)
  3. find:multimap 返回中序第一个匹配 key
  4. erase:按值删除会删除所有相同 key

四、封装红黑树实现 mymap 与 myset

4.1 STL 源码框架分析

  1. STL 中 map/set复用同一棵红黑树 rb_tree
  2. set:红黑树存储Key,纯 key 搜索
  3. map:红黑树存储pair<const Key, T>,键值对搜索
  4. 核心设计:通过模板参数 + 仿函数,让红黑树适配两种存储类型

4.2 前置准备:红黑树基础定义

红黑树节点结构

前文已讲过,这里不过多赘述


4.3 迭代器模拟实现

一、核心思路

红黑树迭代器和 list 迭代器的实现思路一致:

  • 本质 :用一个类封装红黑树节点指针,重载 *->++-- 等运算符,让迭代器表现得像 "智能指针"
  • 核心目标 :实现红黑树的中序遍历(左子树 → 根节点 → 右子树) ,保证 map/set 按 key 升序访问
  • begin():返回中序遍历的第一个节点(红黑树的最左节点)的迭代器

二、operator++(中序下一个节点)的核心逻辑

只看当前节点的局部关系,不用遍历整棵树,分两种情况:

  1. 当前节点有右子树

    • 规则:当前节点访问完,下一个节点是右子树的最左节点
    • 原因:中序遍历中,根节点之后是右子树的最左节点
    • 实现:cur = cur->_right; while (cur->_left) cur = cur->_left
  2. 当前节点没有右子树

    • 规则:沿着 parent 向上回溯,直到找到 "当前节点是父节点的左孩子" 的祖先节点 ,这个祖先就是下一个节点。
    • 原因:当前节点是父节点的右孩子时,父节点已被访问;只有找到 "左孩子父节点",才说明左子树遍历完成,父节点就是下一个要访问的根节点。
    • 实现:while (parent && cur == parent->_right) { cur = parent; parent = parent->_parent; } cur = parent;

三、operator--(中序前一个节点)的核心逻辑

++ 完全对称,访问顺序反过来(右子树 → 根节点 → 左子树):

  1. 当前节点有左子树

    • 下一个节点是左子树的最右节点
  2. 当前节点没有左子树

    • 向上回溯,直到找到 "当前节点是父节点的右孩子" 的祖先节点 '',这个祖先就是前一个节点

四、end() 的实现方式

  1. 简化版实现(我们的手写版)

    • ++ 遍历到最右节点后,继续 ++ 时,向上回溯找不到 "左孩子父节点",最终 parent 会变成 nullptr
    • 此时将迭代器的节点指针置为 nullptr,用 nullptr 表示 end()
    • 注意:反向迭代器 --end() 时,需要特殊处理,直接指向最右节点。
  2. STL 源码实现

    • 红黑树会额外增加一个哨兵位头节点 作为 end()
    • 哨兵节点和根节点互为父节点,左孩子指向最左节点,右孩子指向最右节点。
    • 这种实现更规范,也支持反向迭代器的统一处理。
cpp 复制代码
// 红黑树迭代器
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)
    {}

    // 重载解引用
    Ref operator*()
    {
        return _node->_data;
    }

    // 重载箭头
    Ptr operator->()
    {
        return &_node->_data;
    }

    // 迭代器++ 中序下一个节点
    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--()
    {
        // 省略实现,和++逻辑相反
        return *this;
    }

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

4.4 通用红黑树封装

cpp 复制代码
// 红黑树类
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);
    }

    // 插入核心逻辑
    pair<Iterator, bool> Insert(const T& data)
    {
        // 1. 空树直接插入根节点
        if (_root == nullptr)
        {
            _root = new Node(data);
            _root->_col = BLACK;
            return make_pair(Iterator(_root, _root), true);
        }

        // 2. 寻找插入位置
        KeyOfT kot; // 仿函数取key
        Node* cur = _root;
        Node* parent = nullptr;
        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
            {
                // key重复,插入失败
                return make_pair(Iterator(cur, _root), false);
            }
        }

        // 3. 插入新节点
        cur = new Node(data);
        Node* newnode = cur;
        if (kot(parent->_data) < kot(data))
            parent->_right = cur;
        else
            parent->_left = cur;
        cur->_parent = parent;

        // 4. 红黑树颜色调整+旋转(省略旋转逻辑,沿用之前红黑树代码)
        AdjustColor(newnode);
        _root->_col = BLACK;

        return make_pair(Iterator(newnode, _root), true);
    }

    // 查找节点
    Iterator 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 Iterator(cur, _root);
        }
        return End();
    }

private:
    Node* _root = nullptr; // 根节点
    void AdjustColor(Node* cur)
    {
        // 红黑树插入后颜色调整与旋转逻辑(复用之前实现)
    }
};

4.5 模拟实现 myset

cpp 复制代码
// 模拟实现set
namespace bit
{
    template<class K>
    class set
    {
        // 仿函数:从数据中取出key(set数据本身就是key)
        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& key)
        {
            return _t.Insert(key);
        }

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

    private:
        // 底层复用红黑树:key=K,存储数据=const K
        RBTree<K, const K, SetKeyOfT> _t;
    };
}

4.6 模拟实现 mymap

cpp 复制代码
// 模拟实现map
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;

        iterator begin() { return _t.Begin(); }
        iterator end() { 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);
        }

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

    private:
        // 底层红黑树:存储pair<const K, V>
        RBTree<K, pair<const K, V>, MapKeyOfT> _t;
    };
}

五、总结与对比

5.1 set/multiset 对比

| 容器 | 元素重复 | 排序 | find 返回 | count 返回 | 按值 erase |
| multiset | 允许 | 升序 | 第一个匹配 | 实际个数 | 删除所有匹配 |

set 不允许 升序 唯一元素 0 或 1 删除单个

5.2 map/multimap 对比

| 容器 | key 重复 | 支持 \[\] | 适用场景 |
| multimap | 允许 | 不支持 | 一对多映射 |

map 不允许 支持 一对一键值映射、去重统计

5.3 底层核心设计回顾

  1. map/set 底层均为红黑树,时间复杂度稳定 O(logN)
  2. 通过模板 + 仿函数实现红黑树复用,一套代码适配两种容器
  3. set 只读不可修改,map 可改 value 不可改 key
  4. []是 map 专属多功能接口:插入 + 查找 + 修改三合一

5.4 适用场景选型

  • 只去重排序:选 set
  • 允许重复排序:选 multiset
  • 一对一键值映射:选 map
  • 一对多键值映射:选 multimap
相关推荐
枫叶丹41 小时前
【HarmonyOS 6.0】Live View Kit 实况窗开发详解:进度胶囊支持副文本功能探究
开发语言·华为·harmonyos
不会C语言的男孩1 小时前
C++ Primer Plus 第17章:输入、输出和文件
开发语言·c++
SilentSamsara1 小时前
FastAPI 实战:从路由定义到依赖注入的完整 REST API
开发语言·python·青少年编程·fastapi
Lsk_Smion1 小时前
力扣实训 _ [33].搜索旋转排序数组 _ [92].翻转链表Ⅱ
java·数据结构·算法
j_xxx404_1 小时前
Linux 线程同步硬核解析:从条件变量、阻塞队列到信号量环形队列
linux·运维·服务器·c++·人工智能·ai·中间件
Zhang~Ling1 小时前
二叉搜索树(BST)详解:插入、删除、查找与 Key/Value 实战场景
数据结构·c++·算法
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题 第86题】【Mysql篇】第16题:MySQL 中锁的种类与行锁实现原理?
java·开发语言·数据库·mysql·面试
不爱编程的小陈1 小时前
Go内存模型与GC机制:高性能编程的核心
开发语言·后端·golang
右耳朵猫AI1 小时前
PHP技术周刊 2026年第20周
开发语言·php