跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ STL 有序关联容器详解:set、map 及其变体用法

C++ STL 有序关联容器包括 set、multiset、map 和 multimap,基于红黑树实现,支持高效查找与排序。set 存储唯一键,multiset 允许重复;map 存储键值对,multimap 支持重复键。核心操作涵盖构造、插入、删除、查找及区间遍历。insert 返回 pair 指示是否插入成功,erase 返回后继迭代器。operator[] 在 map 中兼具查找与修改功能。掌握这些容器能显著提升数据处理效率,避免手动实现带来的性能瓶颈。

baireiraku发布于 2026/3/23更新于 2026/10/2473 浏览
C++ STL 有序关联容器详解:set、map 及其变体用法

C++ STL 有序关联容器详解

在 C++ 中,如果需要快速查找一组数据中的唯一元素,或者统计某个键出现的次数,手写数组或列表往往代码冗长且效率低下。STL(标准模板库)中的关联容器——set、multiset、map 和 multimap,正是为解决这类问题而生的。

1. set 容器

文章配图 文章配图

常用接口说明

1.1 构造函数

最常用的构造方式有三种,分别支持迭代器区间、拷贝以及初始化列表:

void test01() {
    vector<int> v({ 4,5,2,9,6,8,3,1,7,7,7,7 });
    set<int> st1(v.begin(), v.end()); // 迭代器区间构造
    set<int> st2(st1);                // 拷贝构造
    set<int> st3({ 4,5,2,9,6,8,3,1,7,7,7,7 }); // 初始化列表构造
}
1.2 迭代器

begin() 和 end() 是最常用的迭代器接口,常用于遍历或作为区间构造的参数。由于 set 底层是平衡二叉搜索树(红黑树),begin() 返回的迭代器指向最小的元素。

void test02() {
    set<int> st({ 4,5,2,9,6,8,3,1,7,7,7,7 });
    auto begin = st.begin(); // 返回第一个节点的迭代器
    auto end = st.end();     // 返回最后一个节点后一个位置的迭代器
    cout << *begin << endl;
    cout << *(--end) << endl; // --end 找到最后一个有效节点
}

为了方便后续观察容器状态,我们可以封装一个简单的打印函数。

1.3 插入 insert

set 底层基于红黑树,插入数据遵循二叉搜索树规则,但不允许插入相同的值。insert 接口的返回值比较特殊,是一个 pair 对象:

  • first: 迭代器。如果插入成功,指向新节点;如果已存在,指向原节点。
  • second: 布尔值。插入成功为 true,失败为 false。
void test03() {
    set<int> st;
    pair<set<int>::iterator, bool> p1, p2;
    
    p1 = st.insert(5);
    st.insert(3);
    p2 = st.insert(5); // 插入已存在的值
    
    cout << *(p1.first) << endl;
    if (p2.second) cout << "原来不存在" << endl;
    else cout << "原来存在" << endl;
}
1.4 删除 erase

删除操作同样返回后继迭代器,被删除节点的迭代器会失效。

void test04() {
    set<int> st({ 4,5,2,9,6,8,3,1,7,7,7,7 });
    Print_container(st);
    set<int>::iterator it = st.erase(st.begin()); // 删除第一个节点
    cout << *it << endl;
}
1.5 查找 find

找到则返回节点迭代器,未找到返回 set::end()。

void test05() {
    set<int> st({ 4,5,2,9,6,8,3,1,7,7,7,7 });
    auto it = st.find(5);
    if(it != st.end()) {
        cout << *it << endl;
    }
}
1.6 统计 count

由于 set 不允许重复值,count 要么返回 1,要么返回 0。因此它也可以用来判断元素是否存在。

void test06() {
    set<int> st({ 4,5,2,9,6,8,3,1,7,7,7,7 });
    cout << st.count(5) << endl;   // 输出 1
    cout << st.count(50) << endl;  // 输出 0
}
1.7 区间查找 lower_bound / upper_bound

这两个接口非常强大,可以根据不存在的值来确定区间范围。

// 删除 [30, 50) 区间内的值
void test07() {
    set<int> st1({ 20,10,50,30,10,90,70,40,80 });
    Print_container(st1);
    
    auto begin1 = st1.find(30);
    auto end1 = st1.find(50);
    while (begin1 != end1) {
        begin1 = st1.erase(begin1);
    }
    Print_container(st1); // 结果:10 20 50 70 80 90
    
    // 使用 lower_bound/upper_bound 删除 [30, 50]
    set<int> st2({ 20,10,50,30,10,90,70,40,80 });
    Print_container(st2);
    auto begin2 = st2.lower_bound(30);
    auto end2 = st2.upper_bound(50);
    while (begin2 != end2) {
        begin2 = st2.erase(begin2);
    }
    Print_container(st2); // 结果:10 20 70 80 90
}

2. multiset 容器

multiset 与 set 的主要区别在于支持插入相同的值。因此部分接口行为有所差异:

  • 查找:find 返回中序遍历序列中的第一个匹配项。
  • 删除:调用 erase(val) 会一次性删除所有值为 val 的节点。
  • 统计:count 可能返回大于 1 的值。

常用接口说明

2.1 插入
void test1() {
    multiset<int> multi;
    multi.insert(3);
    multi.insert(2);
    multi.insert(3);
    multi.insert(3);
    Print_container(multi);
}
2.2 查找

当存在多个相同值时,find 仅返回第一个。若需遍历所有相同值,可结合 equal_range 使用。

2.3 删除

与 set 类似,但针对值的删除会移除所有匹配项。

2.4 统计
void test4() {
    multiset<int> multi;
    multi.insert(3);
    multi.insert(3);
    multi.insert(3);
    multi.insert(3);
    Print_container(multi);
    cout << multi.count(3); // 输出 4
}
2.5 equal_range

返回包含所有相等元素的迭代器区间。

3. map 容器

map 的常用接口逻辑与 set 基本一致,只是操作对象从单一键值变成了键值对(Key-Value)。查找、删除、统计及区间查找均基于键值完成。

常用接口说明

3.1 构造函数

支持默认构造、迭代器区间构造等。

3.2 插入 insert

map 插入时需要提供键值和映射值。利用 pair 可以方便地构造数据:

void test01() {
    map<string, string> dict1;
    pair<string, string> p("first", "第一");
    dict1.insert(p);
    dict1.insert(pair<string, string>("second", "第二"));
    dict1.insert({ "third", "第三" });
    dict1.insert(make_pair("forth", "第四"));
    
    map<string, string> dict2 = { 
        {"left", "左边"}, 
        {"right", "右边"}, 
        {"insert", "插入"},
        {"string", "字符串"} 
    };
    Print_Container(dict1);
    Print_Container(dict2);
}

注意:当插入新节点的键值已存在时,映射值不会更新。

3.3 operator[]

operator[] 功能强大,兼具查找、插入和修改映射值的能力。若键不存在则自动插入默认值,若存在则直接修改映射值。其返回值是映射值的引用。

void test_4() {
    map<string, string> dict;
    dict.insert({ "left", "左边" });
    dict.insert(make_pair("right", "右边"));
    
    dict["sort"];           // 查找 + 插入(默认空字符串)
    dict["string"] = "字符串"; // 查找 + 修改
    dict["left"] = "左边的";   // 查找 + 修改
    
    auto it = dict["left"];
    cout << it << endl;
}

常见场景:统计字符串中元素出现次数。将元素作为键,计数作为映射值,利用 operator[] 的特性即可轻松实现。

4. multimap 容器

multimap 几乎与 map 一样,区别在于支持插入相同键值的节点。

常用接口说明

4.1 插入
void Test01() {
    multimap<string, string> mp;
    mp.insert(make_pair("left", "左边"));
    mp.insert(make_pair("left", "左边"));
    mp.insert(make_pair("left", "左边"));
    Print_Container(mp);
}
4.2 删除
void Test02() {
    multimap<string, string> mp;
    mp.insert(make_pair("left", "左边"));
    mp.insert(make_pair("left", "左边"));
    mp.insert(make_pair("left", "左边"));
    mp.erase("left"); // 删除所有 key 为 left 的节点
    Print_Container(mp);
}

掌握这些容器的特性,能让我们在处理复杂数据结构时更加得心应手,避免重复造轮子。

目录

  1. C++ STL 有序关联容器详解
  2. 1. set 容器
  3. 常用接口说明
  4. 1.1 构造函数
  5. 1.2 迭代器
  6. 1.3 插入 insert
  7. 1.4 删除 erase
  8. 1.5 查找 find
  9. 1.6 统计 count
  10. 1.7 区间查找 lowerbound / upperbound
  11. 2. multiset 容器
  12. 常用接口说明
  13. 2.1 插入
  14. 2.2 查找
  15. 2.3 删除
  16. 2.4 统计
  17. 2.5 equal_range
  18. 3. map 容器
  19. 常用接口说明
  20. 3.1 构造函数
  21. 3.2 插入 insert
  22. 3.3 operator[]
  23. 4. multimap 容器
  24. 常用接口说明
  25. 4.1 插入
  26. 4.2 删除

更多推荐文章

查看全部
  • 程序员发现以太坊关键漏洞获赏 1272 万元
  • Python 读取图片 EXIF 信息解析拍摄地理位置与时间
  • DeepSeek 团队揭秘:清北应届生撑起大模型技术
  • 现代前端 UI 工作流:利用 AI 技能提升设计落地效率
  • UI UX Pro Max:AI 驱动的现代前端 UI 工作流实战
  • 大模型 RAG 架构落地的十大核心挑战
  • 基于 OpenClaw 与飞书构建 AI 新闻推送机器人
  • 基于本地大语言模型与 LangChain 构建免费 AI 搜索问答助手
  • 基于 Jetson 与 OpenClaw 搭建飞书远程交互系统
  • C# WinForm 调用 HeyGem 核心算法 DLL 封装实践
  • C++ 二叉搜索树:原理与增删查实现详解
  • Llama-Factory 集成 Wandb 监控:跨团队训练协作
  • 基于 Spring Boot 3 与 Vue 3 的汽车租赁系统
  • Web 虚拟卡销售平台实现方案
  • SenseVoiceSmall 语音识别 WebUI 快速部署与使用指南
  • MicroG 在 HarmonyOS 上的签名兼容方案与配置指南
  • Processing.py Python 创意编程入门指南
  • JavaEE 深度解析:从 Jakarta EE 演进、B/S 架构到 SSM 框架
  • 通义万相 2.1 结合计算平台实现 AIGC 内容生成
  • 基于 DeepFace 与 OpenCV 的实时情绪分析器实战

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online