1.核心总览
set:有序、唯一的集合,只存单个值(key = value)
map:有序、唯一的键值对,存 key + value(key 唯一,value 可重复)
相同点(底层 + 特性)
底层:红黑树(平衡二叉搜索树)
特性:key 自动升序排序、key 不可重复
效率:查找 / 插入 / 删除 → 稳定 O(log n)
限制:不能修改 key(修改会破坏红黑树结构)
核心区别表格:
| 特性 | set | map |
|---|---|---|
| 存储数据 | 单个值(key) | 键值对(key: value) |
| key 作用 | 既是键也是值 | 唯一索引 |
| 访问方式 | 只能用迭代器 | 支持 [key] 下标访问 |
| 元素类型 | T |
pair<const K, V> |
| 重复元素 | 不允许 | 不允许重复 key |
2.使用示例
set 使用示例:
cpp
void test_set() {
// 1. 定义:set<类型>
set<int> s;
// 2. 插入(自动去重+排序)
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(3); // 重复,自动忽略
// 3. 遍历(迭代器,有序输出:1 2 3)
for (auto it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
// 4. 查找
auto it = s.find(2);
if (it != s.end()) cout << "\n找到元素:" << *it;
// 5. 删除
s.erase(3);
}
map 使用示例:
cpp
void test_map() {
// 1. 定义:map<key类型, value类型>
map<int, string> m;
// 2. 插入(key唯一,自动按key排序)
m.insert(make_pair(3, "张三"));
m[1] = "李四"; // 最常用:[] 插入/修改
m[2] = "王五";
m[3] = "张三丰"; // key重复,覆盖value
// 3. 遍历(有序输出:1 2 3)
for (auto& p : m) {
cout << p.first << ":" << p.second << " ";
}
// 4. 查找
auto it = m.find(2);
if (it != m.end()) cout << "\n找到:" << it->second;
// 5. 删除
m.erase(3);
}
当然头文件也是必须的:
cpp
#include <iostream>
#include <set> // set
#include <map> // map
using namespace std;
3.map的元素
std::map 容器内部存储的每一个元素,都必须是 std::pair<const Key, T> 类型 **,这是 C++ 标准规定的,没有例外!
map 的全称是键值对映射容器,它必须存储 key(键
) + value(值) 两部分。
C++ 用 std::pair 这个结构体来绑定这两部分:
cpp
// map 内部固定的元素类型(背下来)
using value_type = pair<const Key, T>;
pair.first → key(带 const,不能修改!)
pair.second → value(可以修改)
3 种插入方式,本质全是 pair:
cpp
map<int, string> m;
// 方式1:[] 运算符(最常用)→ 底层自动生成 pair
m[1] = "张三";
// 方式2:花括号初始化 → C++11 隐式转 pair
m.insert({2, "李四"});
// 方式3:显式写 pair → 最原始的写法
m.insert(pair<int, string>(3, "王五"));
不管怎么写,存进 map 里的都是 pair<const int, string>
遍历 map 时,必须用 pair 接收:
cpp
// 遍历 map,迭代器指向的就是 pair!
for (auto& element : m) {
// element 的类型就是 pair<const int, string>
cout << element.first << " : " << element.second << endl;
}
如果元素不是 pair,你根本无法用 .first / .second 访问!
① 为什么是 const Key?
map 底层是红黑树,key 用来排序,一旦插入就不能改,否则会破坏树结构。
所以 pair 的第一个成员强制 const,禁止修改 key。
② 可以存自定义类型吗?
可以!但依然要包装成 pair:
cpp
// 自定义结构体
struct Student { int id; string name; };
// map 元素依然是 pair:key是int,value是自定义结构体
map<int, Student> m;
m[101] = {101, "小明"};
4.核心区别
| 容器 | 存储元素 | 结构 | 有无 .first/.second |
|---|---|---|---|
| map | pair<const K, V> |
键 + 值 | ✅ 有 |
| set | const T |
单个值 | ❌ 无 |
5.运用场景
1.std::set 适用场景
核心:只需要存【单个值】、要求【自动去重】、【有序】、高频判断【是否存在】
关键词:去重、存在性检查、有序集合、无映射关系。
1. 黑名单 / 白名单
场景:判断用户、IP、设备是否在禁止列表里
cpp
set<string> black_list;
black_list.insert("192.168.1.1");
// 核心需求:查 有没有!
if(black_list.count("192.168.1.1")) {
cout << "禁止访问";
}
只用存一个 IP,不需要对应其他数据 → set 完美
2.数据自动去重 + 排序
场景:收集学生学号、订单号,要求不重复、自动排好序
cpp
set<int> ids;
ids.insert(1001);
ids.insert(1003);
ids.insert(1001); // 重复,自动忽略
3.有序唯一集合
场景:排行榜、分类标签、单词去重统计
2.std::map 适用场景
核心:需要存【键值对】、通过 key 快速找 value、key 有序、一一映射
关键词:映射关系、查值、ID→信息、配置表
1. 唯一标识 → 详细信息
场景:用户 ID 查用户信息、学号查学生、工号查员工
cpp
map<int, Student> student_map;
student_map[1001] = {"张三", 18, "计算机"};
// 核心需求:用学号找学生详情
auto stu = student_map[1001];
必须存 key + value → 只能用 map
2. 统计计数
场景:统计单词出现次数、商品销量、点击量
cpp
map<string, int> count_map;
count_map["hello"]++; // 单词 hello 计数+1
3. 有序配置 / 字典
场景:配置文件(key = 配置名,value = 值)、有序字典
极简对比表:
| 需求 | 选择 | 例子 |
|---|---|---|
| 存 IP 黑名单 | set | 只存 IP,查是否存在 |
| 学号→学生信息 | map | 一个学号对应一个学生对象 |
| 单词去重 | set | 只保留唯一单词 |
| 单词出现次数 | map | 单词 → 次数 |
| 有序编号 | set | 自动排序不重复 |
| 用户 ID→昵称 | map | 一一映射 |
谢谢