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

C++ 关联容器实战:Set、Map 与键值对详解

C++ 关联式容器通过键值对存储数据,分为树形(红黑树)和哈希结构。set/multiset 基于红黑树维护有序唯一或可重复元素,map/multimap 则提供 key-value 映射。掌握迭代器遍历、元素插入删除及 lower_bound/upper_bound 范围操作是核心。注意 map 的 [] 操作符在 key 不存在时会隐式创建默认值,而 at() 会抛异常。理解底层实现有助于选择合适容器优化检索性能。

ByteFlow发布于 2026/3/26更新于 2026/6/220 浏览
C++ 关联容器实战:Set、Map 与键值对详解

关联式容器概述

在 C++ STL 中,vector、list、deque 等属于序列式容器,底层是线性结构,存储的是元素本身。而关联式容器虽然也是用来存储数据,但它的核心在于**<key, value>结构的键值对**。这使得它在数据检索上比序列式容器效率更高,典型的如 set、map、unordered_set、unordered_map 等。

需要区分的是,stack、queue 和 priority_queue 属于容器适配器,它们默认的基础容器分别是 deque、vector 等,并不直接归类为关联式容器。

树形结构与哈希结构

根据应用场景不同,STL 实现了两种主要结构的关联式容器:

关联式容器容器结构底层实现
set, map, multiset, multimap树型结构平衡搜索树 (红黑树)
unordered_set, unordered_map...哈希结构哈希表

树型结构中的元素是有序的,适合需要遍历有序数据的场景;哈希结构则是无序的,追求极致的查找速度。

键值对基础

键值对(Key-Value Pair)表示具有一一对应关系的结构,通常包含两个成员变量:key 代表索引或标识,value 表示与 key 对应的具体信息。这是关联式容器的核心存储单元。

Set 与 Multiset

Set 的使用

set 是按照特定次序存储元素的容器。在 set 中,元素的 value 同时也作为 key 存在,且每个 value 必须是唯一的。这意味着 set 中的元素不能在容器中修改(元素总是 const),但可以从容器中插入或删除。

内部而言,set 的元素总是按照比较对象(类型比较)指示的严格弱排序准则进行排序。由于底层基于二叉搜索树(通常是红黑树)实现,通过 key 访问单个元素的速度通常比 unordered_set 慢,但它允许根据顺序对子集进行直接迭代。

构造方式
#include <iostream>
#include <set>
#include <vector>
using namespace std;

void testSetConstructor() {
    // 构造空的 set
    set<int> s1;
    
    // 迭代器区间构造,会自动去重并排序
    vector<int> v1 = { 1,2,3,4,5,5,6,7,7 };
    set<int> s2(v1.begin(), v1.end());
    
    // 拷贝构造
    set<int> s3(s2);
    
    for(auto & element : s2) cout << element << " ";
    cout << endl;
    for (auto& element : s3) cout << element << " ";
    cout << endl;
}
迭代器与容量

set 支持正向和反向迭代器。容量操作包括 size() 获取元素个数,empty() 判断是否为空。

void testSetCapacity() {
    vector<int> v1 = { 1,8,9,7,5,6,4,3,2 };
    set<int> s1(v1.begin(), v1.end());
    
    cout << "Size: " << s1.size() << endl;
    cout << "Empty: " << s1.empty() << endl;
}
修改与删除

set 不支持修改现有元素,因为它是有序的,修改 key 会破坏树的平衡。我们只能通过 insert 插入新元素,或通过 erase 删除元素。

void testSetModification() {
    set<int> s1;
    s1.insert(1); s1.insert(2); s1.insert(3);
    s1.insert(5); s1.insert(6); s1.insert(7);
    s1.insert(25);
    
    for (auto& element : s1) cout << element << " ";
    cout << endl;
    
    // 使用 find 定位后删除
    set<int>::iterator Position = s1.find(2);
    if(Position != s1.end()) s1.erase(Position);
    
    // 直接通过 key 删除,返回删除的元素个数
    size_t count = s1.erase(3);
    cout << "Deleted count: " << count << endl;
    
    // count 方法用于检查元素是否存在
    cout << "25 exists: " << s1.count(25) << endl;
}
范围操作

lower_bound 和 upper_bound 提供了强大的范围查询能力。lower_bound(key) 返回第一个大于等于 key 的迭代器,upper_bound(key) 返回第一个大于 key 的迭代器。结合这两个函数,可以轻松实现闭区间或半开区间的删除。

void testSetRangeOps() {
    set<int> s1;
    for(int i=0; i<10; ++i) s1.insert(i * 10);
    
    // 获取 [25, 80) 范围内的迭代器
    auto itlow = s1.lower_bound(25);
    auto itup = s1.upper_bound(70);
    
    // 删除该范围内的所有元素
    s1.erase(itlow, itup);
    
    for (auto& element : s1) cout << element << " ";
    cout << endl;
}

Multiset

multiset 与 set 类似,也是按特定顺序存储元素,但允许元素重复。底层同样是红黑树。

void testMultiSet() {
    multiset<int> s1;
    s1.insert(1); s1.insert(1); s1.insert(3);
    s1.insert(2); s1.insert(5);
    
    for (auto& element : s1) cout << element << " ";
    // 输出结果:1 1 2 3 5
}

注意:multiset 的元素同样不能被修改,只能插入或删除。

Map 与 Multimap

Map 的使用

map 是关联容器,按照特定的次序(通常按 key 比较)存储由键值 key 和值 value 组合而成的元素。在 map 内部,key 与 value 绑定在一起,类型为 pair<const Key, T>。

关键点:

  1. Key 唯一性:map 中的 key 是唯一的,且不能修改。
  2. 有序性:遍历时可以得到一个有序的序列。
  3. 下标访问:支持 operator[],如果 key 不存在,会自动创建默认 value 并插入。
  4. 底层实现:平衡二叉搜索树(红黑树)。
迭代器与访问

map 的迭代器指向 pair<const Key, T> 类型的元素。可以通过 .first 访问 key,.second 访问 value。

void testMapIterator() {
    map<string, string> m1;
    m1.insert({"Begin", "开始"});
    m1.insert(make_pair("End", "结束"));
    
    map<string, string>::iterator it = m1.begin();
    while (it != m1.end()) {
        cout << it->first << ": " << it->second << endl;
        ++it;
    }
}
元素访问与修改

在使用 operator[] 时需注意:如果 key 不存在,它会先插入一个默认构造的 value,然后返回引用。如果需要安全地访问而不想触发插入,应使用 at() 方法,当 key 不存在时会抛出异常。

void testMapAccess() {
    map<string, string> dict;
    dict["string"] = "字符串";
    
    // operator[] 若 key 不存在会插入默认值
    string val = dict["right"];
    
    // at() 若 key 不存在会抛异常
    try {
        string safeVal = dict.at("left");
    } catch (...) {
        cout << "Key not found via at()" << endl;
    }
}

删除元素可以使用 erase(key) 或 erase(iterator)。

void testMapModify() {
    map<string, string> Dictionary;
    Dictionary.insert({"apple","苹果"});
    Dictionary.insert({"banana", "香蕉"});
    Dictionary.insert({"watermelon", "西瓜"});
    
    // 删除指定 key
    Dictionary.erase("lemon"); // 如果 lemon 不存在,count 为 0,不会报错
    Dictionary.erase("watermelon");
    
    for (auto& Kv : Dictionary) 
        cout << Kv.first << ": " << Kv.second << endl;
}

Multimap

multimap 与 map 的唯一区别在于:key 是可以重复的。它常用于统计词频等场景。

void testMultiMap() {
    string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果" };
    multimap<int, string> SortMap;
    map<string, int> CountMap;
    
    // 先统计频率
    for (auto& element : arr) CountMap[element]++;
    
    // 再按频率排序存入 multimap
    for (auto& kv : CountMap) 
        SortMap.insert({kv.second, kv.first});
    
    for (auto& kv : SortMap) 
        cout << kv.first << ": " << kv.second << endl;
}

注意:multimap 没有重载 operator[],因为 key 不唯一,无法确定返回哪个 value。

目录

  1. 关联式容器概述
  2. 树形结构与哈希结构
  3. 键值对基础
  4. Set 与 Multiset
  5. Set 的使用
  6. 构造方式
  7. 迭代器与容量
  8. 修改与删除
  9. 范围操作
  10. Multiset
  11. Map 与 Multimap
  12. Map 的使用
  13. 迭代器与访问
  14. 元素访问与修改
  15. Multimap
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • HarmonyOS 视频封面智能生成与 AI 集成实战
  • 无经验如何成为 AI 产品经理
  • Tomcat 下载、安装与环境配置实战指南
  • Python 实现 MCP 客户端调用高德地图天气查询
  • 网络安全入门:新手如何从零开始学习漏洞挖掘
  • MySQL 数据类型选型实战与避坑指南
  • 深入理解 MySQL 索引:核心原理与实战优化指南
  • Python 异步编程与协程详解
  • N46Whisper:基于 Whisper 的日语语音转字幕工具
  • OpenClaw 系列 16 款 AI Agent 工具选型指南
  • 基于Vivado的AD9680 FPGA测试程序开发:1G采样率与Lane4配置
  • 利用腾讯云 HAI 与 DeepSeek 快速构建个人网页
  • Python 环境安装与配置 Gurobi 求解器指南
  • 法律领域自然语言处理(NLP)应用与实战指南
  • 基于 MCP 和 Skill 的前端 JS 逆向自动化落地实践
  • AI 大模型在制造业中的应用类型与核心能力
  • OpenClaw 多智能体架构配置指南
  • GTC 2026 前瞻:Rubin 平台与 AI 工厂化趋势
  • Java 使用 Jackson 解析 JSON 数据示例
  • Trae、Cursor、Copilot、Windsurf 深度对比评测

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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