C++ 关联式容器map 与 set 的原理与实践操作
在 C++ 中,容器是存放数据的重要数据结构,分为序列式容器和关联式容器。序列式容器(如 vector、list、deque)按线性顺序存储元素,元素的位置与值无关;而关联式容器则通过键(key)建立元素间的关联,实现高效的查找、插入和删除操作。本文将详细介绍关联式容器中最常用的 map 和 set,包括它们的底层实现、核心特性、使用方法及实际应用。
一、关联式容器的核心概念
1. 容器分类与特点
关联式容器的核心是 “关联关系”,即通过键(key)快速定位元素,而无需像序列式容器那样遍历整个容器。其特点如下:
- 元素按特定规则排序(有序容器)或无序存储(无序容器);
- 插入位置由元素的键决定,而非用户指定;
- 查找效率极高,平均时间复杂度为 O(logN)(有序容器)或 O(1)(无序容器)。
2. 底层实现
有序容器(set、map 等)的底层通常采用 平衡二叉搜索树(红黑树) 实现,其特性为:
- 左子树所有节点的值 < 根节点的值;
- 右子树所有节点的值 > 根节点的值;
- 树的高度保持平衡,确保查找、插入、删除操作的时间复杂度为 O(logN)。
无序容器(unordered_set、unordered_map 等)的底层采用 哈希表 实现,通过哈希函数将键映射到存储位置,平均时间复杂度为 O(1),但最坏情况下可能退化为 O(N)。
3. 搜索模型
关联式容器分为两种搜索模型:
- K 模型:仅存储键(key),如 set,核心功能是判断元素是否存在;
- KV 模型:存储键值对(key-value),如 map,核心功能是通过键查找对应的值。
二、set 的原理与使用
1. set 的核心特性
set 是 有序、不重复 的 K 模型容器,底层为红黑树。其核心特性:
- 自动排序:插入元素后,容器会按键的升序(默认)排列;
- 自动去重:插入重复元素时,操作会失败,容器中仅保留一个实例;
- 不可修改元素:set 中的元素是 const 类型,修改元素会破坏红黑树的结构,需通过 “删除旧元素 + 插入新元素” 实现。
2. set 的常用操作
(1)插入操作
set 不支持 push_back/push_front,需使用 insert() 插入元素:
|
1 2 3 4 5 6 |
|
插入后,set 中的元素会自动排序为 {1, 3}。
(2)遍历操作
set 支持迭代器遍历和范围 for 遍历:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
(3)删除操作
set 支持两种删除方式:
- 通过迭代器删除(需先通过
find()查找元素); - 直接通过值删除。
|
1 2 3 4 5 6 7 8 9 |
|
(4)查找操作
set 的查找功能是其核心,提供两种方式:
- 成员函数
find():利用红黑树特性,时间复杂度 O(logN); - 算法
std::find():线性遍历,时间复杂度 O(N)。
示例对比:
|
1 2 3 4 5 |
|
使用建议:优先使用 set 的成员函数 find() 以获得最佳性能。
3. set 的实际应用
set 的核心优势是 快速存在性检查 和 高效去重排序,适用于以下场景:
- 存储学号、身份证号等唯一标识,快速验证是否存在;
- 对输入数据去重并排序,如统计考试成绩的不重复分数;
- 实现集合运算(交集、并集、差集)。
示例:验证学号是否存在
|
1 2 3 4 5 6 7 8 9 |
|
三、map 的原理与使用
1. map 的核心特性
map 是 有序、键唯一 的 KV 模型容器,底层为红黑树。其核心特性:
- 存储键值对(key-value),键(key)唯一,值(value)可重复;
- 按键自动排序(默认升序);
- 通过键快速查找对应的值,时间复杂度 O(logN);
- 支持通过键修改值,但键不可修改(否则会破坏红黑树结构)。
2. map 的常用操作
(1)pair 类型介绍
map 中的元素是 pair<const key_type, value_type> 类型,pair 是一个模板结构体,包含两个成员:
first:键(key),不可修改;second:值(value),可修改。
创建 pair 的方式:
|
1 2 3 4 |
|
(2)插入操作
map 通过 insert() 插入 pair 类型元素:
|
1 2 3 4 5 6 7 8 9 |
|
插入后,map 会按键的升序排列:{1:张三, 2:李四, 3:王五}。
(3)遍历操作
map 支持迭代器遍历和范围 for 遍历,通过 it->first 访问键,it->second 访问值:
|
1 2 3 4 5 6 7 8 9 10 |
|
(4)查找与修改操作
通过键查找值有两种方式:
- 成员函数
find():返回指向该键值对的迭代器; - 下标运算符
[]:直接通过键访问值(若键不存在,会自动插入一个默认构造的键值对)。
|
1 2 3 4 5 6 7 8 9 |
|
(5)删除操作
map 的删除方式与 set 类似,支持迭代器删除和键删除:
|
1 2 3 4 5 6 7 |
|
3. map 的实际应用
map 的核心优势是 通过键快速查找值,适用于以下场景:
- 存储键值对映射关系,如字典(单词 - 翻译)、学号 - 成绩;
- 统计元素出现次数,如统计字符串中每个单词的出现次数;
- 实现缓存机制(键为缓存 key,值为缓存数据)。
示例:统计字符串出现次数
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
四、map 与 set 的区别与联系
1. 相同点
- 底层均为红黑树(有序容器),操作时间复杂度均为 O(logN);
- 均支持自动排序和去重(set 去重键,map 去重键);
- 均不支持直接修改元素(set 元素不可修改,map 键不可修改)。
2. 不同点
| 特性 | set | map |
|---|---|---|
| 存储类型 | 仅键(K 模型) | 键值对(KV 模型) |
| 核心功能 | 快速存在性检查 | 快速键值映射查找 |
| 元素访问 | 仅访问键 | 访问键和值 |
| 修改方式 | 不可修改,需删插 | 可修改值,键不可改 |
五、使用注意事项
- 元素不可修改:set 的元素和 map 的键均为 const 类型,修改会破坏红黑树结构,需通过 “删插” 实现;
- 迭代器有效性:插入元素时,红黑树可能重新平衡,迭代器不会失效;删除元素时,仅被删除元素的迭代器失效,其他迭代器有效;
- 比较规则:默认按键的升序排序,若需自定义排序,可在定义容器时指定比较函数(如
set<int, greater<int>>按降序排序); - 效率选择:
- 需有序存储且高效查找时,使用 set/map;
- 无需排序且追求极致查找效率时,使用 unordered_set/unordered_map(哈希表实现);
- 仅需线性存储时,使用 vector/list 等序列式容器。
六、总结
map 和 set 是 C++ 中最常用的关联式容器,其核心优势在于 高效的查找、插入和删除操作,底层依赖红黑树实现有序存储和去重。set 专注于 “键的存在性检查”,map 专注于 “键值对的映射查找”,二者在实际开发中应用广泛,如数据去重、统计计数、字典映射等场景。
掌握 map 和 set 的使用,需理解其底层实现原理和核心特性,根据实际需求选择合适的容器,以优化程序性能。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)