目录
[Map 和 Set 的详细对比](#Map 和 Set 的详细对比)
关联式容器
在C++初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、 forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面 存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
关联式容器 也是用来存储数据的,与序列式容器不同的是,其里面存储的是结构的 键值对,在数据检索时比序列式容器效率更高。
键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代 表键值,value表示与key对应的信息.
树形结构的关联式容器
根据应用场景的不同,STL总共实现了两种不同结构的关联式容器:树型结构与哈希结构。树型结 构的关联式容器主要有四种:map、set、multimap、multiset 。这四种容器的共同点是:使 用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一主要介绍Set和Map.
Set
核心概念
Set 是唯一值的集合,类似于数学中的集合:
- 值唯一,不允许重复(因此可以使用set进行去重)
- 保持插入顺序
- 提供集合运算方法
- 判断值存在性比数组高效
- set在底层是用二叉搜索树(红黑树)实现的
需要注意的是:
- 与map/multimap不同,map/multimap中存储的是真正的键值对,set中只放 value,但在底层实际存放的是由构成的键值对。
- set中的元素不允许修改
- set中的元素默认按照小于来比较
- Set 的实现相对简单,本质上可以看作一个键与值相同的 Map
set的使用
set的模板参数列表

- T: set中存放元素的类型,实际在底层存储的键值对。
- Compare:set中元素默认按照小于来比较
- Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
set的基本操作
cpp
#include <set>
#include <iostream>
int main() {
// 创建
set<int> s1 = {3, 1, 4, 1, 5, 9}; // {1, 3, 4, 5, 9}
// 插入
s1.insert(2);
s1.insert(3); // 重复,不会插入
auto result = s1.insert(6);
if (result.second) {
cout << "Insert succeeded" << endl;
}
// 删除
s1.erase(4); // 删除值为4的元素
auto it = s1.find(5);
if (it != s1.end()) {
s1.erase(it);
}
// 查找
if (s1.find(3) != s1.end()) {
cout << "3 found in set" << endl;
}
// 计数(总是0或1)
cout << "Count of 3: " << s1.count(3) << endl;
// 遍历
for (const auto& val : s1) {
cout << val << " ";
}
cout << endl;
return 0;
}
集合运算
cpp
set<int> setA = {1, 2, 3, 4, 5};
set<int> setB = {3, 4, 5, 6, 7};
// 并集
set<int> unionSet;
set_union(setA.begin(), setA.end(),
setB.begin(), setB.end(),
inserter(unionSet, unionSet.begin()));
// 交集
set<int> intersectionSet;
set_intersection(setA.begin(), setA.end(),
setB.begin(), setB.end(),
inserter(intersectionSet, intersectionSet.begin()));
// 差集
set<int> differenceSet;
set_difference(setA.begin(), setA.end(),
setB.begin(), setB.end(),
inserter(differenceSet, differenceSet.begin()));
// 对称差集
set<int> symmetricDifference;
set_symmetric_difference(setA.begin(), setA.end(),
setB.begin(), setB.end(),
inserter(symmetricDifference, symmetricDifference.begin()));
自定义比较函数
cpp
// 按字符串长度排序
struct LengthCompare {
bool operator()(const string& a, const string& b) const {
if (a.length() == b.length()) {
return a < b; // 长度相同按字典序
}
return a.length() < b.length();
}
};
set<string, LengthCompare> lengthSet;
lengthSet.insert("apple");
lengthSet.insert("banana");
lengthSet.insert("cat");
lengthSet.insert("dog");
// 顺序: cat, dog, apple, banana
std::multiset
简单来说就是允许重复值的set
Map
核心概念
- 基于红黑树实现
- 键值对存储(pair<const Key, T>)
- map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
- 键唯一,自动排序(在内部,map中的元素总是按照键值key进行比较排序)
- 插入/删除/查找:O(log n)
map的使用
map的模板参数

- key: 键值对中key的类型
- T: 键值对中value的类型
- Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比 较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户 自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
- Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的 空间配置器
map的基本操作
cpp
// 1. 默认构造函数
map<int, string> map1;
// 2. 使用初始化列表
map<int, string> map2 = {
{1, "Alice"},
{2, "Bob"},
{3, "Charlie"}
};
// 3. 范围构造函数
map<int, string> map3(map2.begin(), map2.end());
// 4. 拷贝构造函数
map<int, string> map4(map2);
/////////////////////////////////////////////////////////////////
// 插入元素
map<int, string> m;
// 1. insert() 方法
m.insert({1, "Alice"}); // C++11
m.insert(make_pair(2, "Bob")); // C++98
m.insert(pair<int, string>(3, "Charlie"));
// 2. emplace() 方法(C++11,效率更高)
m.emplace(4, "David"); // 直接构造,避免临时对象
// 3. [] 操作符(如果键不存在会创建)
m[5] = "Eve"; // 插入或修改
m[1] = "Alice Updated"; // 修改已存在的键
// 访问元素
cout << m[1] << endl; // 如果键不存在会创建默认值
cout << m.at(1) << endl; // 如果键不存在抛出 out_of_range 异常
// 检查元素是否存在
if (m.find(1) != m.end()) {
cout << "Key 1 exists" << endl;
}
// 删除元素
m.erase(1); // 删除键为1的元素
auto it = m.find(2);
if (it != m.end()) {
m.erase(it); // 通过迭代器删除
}
m.erase(m.begin(), m.end()); // 删除范围
m.clear(); // 清空所有
// 大小和容量
cout << "Size: " << m.size() << endl;
cout << "Empty: " << m.empty() << endl;
cout << "Max size: " << m.max_size() << endl;
查找操作
cpp
map<int, string> m = {{1, "A"}, {2, "B"}, {3, "C"}};
// 1. find() - O(log n)
auto it = m.find(2);
if (it != m.end()) {
cout << "Found: " << it->second << endl;
}
// 2. count() - 返回键的数量(对于map总是0或1)
if (m.count(2) > 0) {
cout << "Key 2 exists" << endl;
}
// 3. lower_bound() - 第一个不小于key的元素
auto lb = m.lower_bound(2);
if (lb != m.end()) {
cout << "Lower bound: " << lb->first << endl;
}
// 4. upper_bound() - 第一个大于key的元素
auto ub = m.upper_bound(2);
if (ub != m.end()) {
cout << "Upper bound: " << ub->first << endl;
}
// 5. equal_range() - 返回pair<lower_bound, upper_bound>
auto range = m.equal_range(2);
for (auto it = range.first; it != range.second; ++it) {
cout << it->first << ": " << it->second << endl;
}
自定义比较函数
cpp
// 1. 函数对象
struct Compare {
bool operator()(const string& a, const string& b) const {
return a.length() < b.length(); // 按长度排序
}
};
map<string, int, Compare> lengthMap;
lengthMap["aaa"] = 1;
lengthMap["bb"] = 2;
lengthMap["c"] = 3;
// 2. 使用lambda表达式(C++11)
auto cmp = [](const string& a, const string& b) {
return a.length() < b.length();
};
map<string, int, decltype(cmp)> lambdaMap(cmp);
// 3. 降序排列
map<int, string, greater<int>> descMap;
descMap[1] = "A";
descMap[2] = "B";
descMap[3] = "C"; // 顺序:3, 2, 1
std::multimap
简单来说就是允许重复键的map
Map 和 Set 的详细对比
本质区别:存储内容
Map(映射):
Map 存储的是键值对,就像一个字典。每个元素都是一个对组,包含键和值两部分。键是唯一的标识符,值是与该键相关联的数据。你可以通过键快速查找到对应的值,就像通过单词查找字典中的解释一样。
Set(集合):
Set 存储的是唯一的值,就像一个数学集合。它只关心值本身,不存储额外的关联信息。Set 的主要目的是确保集合中的元素是唯一的,并快速检查某个值是否存在于集合中。
简单来说:
Map 是关系的容器,它建立键和值之间的映射。它回答的问题是:"对于这个键,对应的值是什么?" Map 强调两个不同实体之间的连接,适合存储丰富的关联信息。
Set 是元素的容器,它维护一个唯一值的集合。它回答的问题是:"这个值是否存在?" Set 强调元素本身和集合的数学性质,适合去重、存在性检查和集合运算。
简单对比表格:
|-----------|-------------------------|--------------------|
| 维度 | Map(映射) | Set(集合) |
| 存储内容 | 键值对(key-value pairs) | 唯一值(unique values) |
| 数据结构 | 字典/映射表 | 数学集合 |
| 核心用途 | 建立键到值的映射关系 | 维护不重复的元素集合 |
| 典型问题 | "键X对应的值是什么?" | "值X是否存在?" |
| C++示例 | std::map<int, string> | std::set<int> |