C++ STL 背诵版知识点总结
一、STL容器总览
口诀:三容器两适配,序列关联加无序
1.1 容器分类
- 序列容器:vector(动态数组)、list(双向链表)、deque(双端队列)、array(固定数组)、forward_list(单向链表)
- 关联容器:set(有序唯一集合)、map(有序键值对)、multiset(有序可重复集合)、multimap(有序可重复键值对)
- 无序容器:unordered_set、unordered_map、unordered_multiset、unordered_multimap
- 容器适配器:stack(栈)、queue(队列)、priority_queue(优先队列)
1.2 底层结构与性能对比
- vector/deque: 动态数组,支持随机访问
- list/forward_list: 链表,高效插入删除
- set/map: 红黑树,有序且查找/插入为O(logN)
- unordered_*: 哈希表,平均O(1)查找/插入,无序
二、序列容器详解
口诀:vector动态连续,list插入灵活,deque双端高效,array固定性能高
2.1 vector(动态数组)
- 核心特性:连续内存存储,随机访问O(1),尾部插入删除高效
- 内存管理:通过三个指针维护:start(起始)、finish(当前末尾)、end_of_storage(容量末尾)
- 扩容机制:空间不足时,分配1.5~2倍新内存(GCC为2倍,VS为1.5倍),复制/移动元素后释放旧内存
- push_back vs emplace_back
- push_back:复制/移动已创建对象到容器末尾
- emplace_back:直接在容器末尾原地构造对象,避免临时对象开销
2.2 list(双向链表)
- 核心特性:节点不连续存储,每个节点包含前后指针,支持双向遍历
- 性能特点:任意位置插入删除O(1),不支持随机访问(O(n))
- 插入删除实现:创建新节点并调整相邻节点指针;删除时释放目标节点并重连指针
- 缓存友好性:节点分散导致缓存命中率低,遍历速度慢于vector
2.3 deque(双端队列)
- 核心结构:中控器(map数组)+多个缓冲区,每个缓冲区存储固定数量连续元素
- 性能特点:头尾插入删除O(1),中间插入删除O(n),随机访问O(1)
- 与vector区别:deque无需整体扩容(只需添加新缓冲区),但随机访问稍慢
2.4 array(固定数组)
- 核心特性:编译时固定大小,栈上分配内存(全局/动态分配除外)
- 使用场景:元素数量固定、性能要求高、无需扩容的场景
- 对比vector:无需动态内存管理,访问速度更快,但大小不可变
三、关联容器详解
口诀:map键值对,set存键值,multi允重复,查找logN
3.1 map与set共性
- 底层实现:均基于红黑树,保证元素有序存储
- 性能特点:查找、插入、删除操作均为O(logN)
- 迭代器特性:遍历有序,且插入/删除不影响其他元素的迭代器(节点地址不变)
3.2 map与set区别
- 存储内容:map存储键值对(pair<const Key, T>),set仅存储键(元素)
- 访问方式:map支持operator[]通过键访问值,set只能通过迭代器访问
- 用途不同:map适用于字典/映射场景,set适用于去重/集合操作
3.3 multimap与multiset
- 核心特点:允许键重复,其他特性与map/set类似
- 排序规则:multimap中相同键的元素按值排序;multiset中相同值的元素内部有序
- 查找方法:需要使用lower_bound/upper_bound/equal_range查找范围
3.4 元素查找方法
- find():查找特定键,返回迭代器,未找到则返回end()
- count():返回指定键的元素个数(map/set返回0或1)
- lower_bound():返回不小于给定键的首个元素迭代器
- upper_bound():返回大于给定键的首个元素迭代器
- equal_range():返回包含lower_bound和upper_bound的迭代器对
四、无序容器详解
口诀:无序基于哈希表,平均查找O(1),无自动排序
4.1 unordered_map与map区别
- 底层实现:unordered_map基于哈希表,map基于红黑树
- 性能差异:unordered_map平均O(1)查找/插入,最坏O(n);map稳定O(logN)
- 有序性:map自动按键排序,unordered_map无序
- 内存开销:unordered_map需额外空间存储哈希表桶
- 迭代器失效:unordered_map在扩容时所有迭代器失效,map仅被删除元素的迭代器失效
五、容器适配器
口诀:stack后进先出,queue先进先出,priority_queue优先级
5.1 stack(栈)
- 底层实现:默认基于deque实现,也可使用list或vector
- 核心操作:push(入栈)、pop(出栈)、top(访问栈顶)、empty(判空)
- 特点:后进先出(LIFO),仅允许访问栈顶元素
5.2 queue(队列)
- 底层实现:默认基于deque实现,也可使用list
- 核心操作:push(入队)、pop(出队)、front(队首)、back(队尾)、empty(判空)
- 特点:先进先出(FIFO),允许访问队首和队尾
5.3 priority_queue(优先队列)
- 底层实现:基于vector实现的二叉堆结构
- 核心特性:自动保持最大元素在队首(默认大顶堆)
- 性能特点:插入O(logN),获取最大元素O(1)
- 应用场景:任务调度、事件处理等需要优先级的场景
六、迭代器详解
口诀:迭代器是容器指针,不同容器类型异,失效规则要牢记
6.1 迭代器类型与功能
- 输入迭代器:只读一次,只能递增
- 输出迭代器:只写一次,只能递增
- 前向迭代器:可读写多次,只能递增(如forward_list)
- 双向迭代器:可读写多次,可递增递减(如list、map、set)
- 随机访问迭代器:可读写多次,支持随机访问(如vector、deque、array)
6.2 迭代器失效情况
- vector:插入/删除元素可能导致插入点之后的迭代器失效;扩容时所有迭代器失效
- deque:头部/尾部插入可能使迭代器失效(取决于实现);中间插入使所有迭代器失效
- list:插入不影响任何迭代器;删除只影响被删除元素的迭代器
- map/set:插入/删除不影响其他元素的迭代器
- unordered_*:插入可能导致扩容,使所有迭代器失效;删除只影响被删除元素的迭代器
七、STL容器使用场景总结
口诀:vector随机访问,list频繁插入,map有序映射,unordered_map高效查找
- vector:需要随机访问、动态扩容的场景(如vector
scores) - list:频繁在中间插入删除的场景(如list
history) - deque:需要双端操作的场景(如deque
window) - map:需要按键排序的键值对场景(map<string, int> dict)
- unordered_map:需要高效查找的键值对场景(unordered_map<int, User> users)
- set:需要去重且有序的集合场景(set
unique_ids) - priority_queue:需要优先级处理的场景(priority_queue
tasks)
八、重要概念辨析
口诀:resize改大小,reserve预分配,erase返下一,迭代器失效各不同
8.1 resize()与reserve()区别
- resize(n):改变容器大小,若n大于当前size则创建新元素,否则销毁多余元素
- reserve(n):预分配内存但不创建元素,只改变capacity,不改变size
- 使用建议:预先知道元素数量时,使用reserve()避免多次扩容
8.2 erase()返回值与迭代器更新
- vector/list/deque:erase(iter)返回指向下一个元素的迭代器
- map/set:erase(iter)不返回值,需手动更新迭代器
- 使用示例:
1
2
3
4
5
6
7
8// 正确遍历删除方式
for (auto it = container.begin(); it != container.end();) {
if (need_remove(*it)) {
it = container.erase(it); // 使用返回值更新迭代器
} else {
++it;
}
}
8.3 map/set迭代器不失效原因
- 基于红黑树实现,插入/删除操作仅调整节点间的指针,不移动节点位置
- 被删除的节点迭代器失效,但其他节点的迭代器不受影响
- 因此可以安全地在遍历过程中删除元素(需正确更新迭代器)
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.