四、STL容器与数据结构(共17项)
1.C/C++中常用容器功能汇总
2.介绍一下vector优缺点
3.介绍一下list优缺点
4.介绍一下deque优缺点
5.介绍一下map&set优缺点
6.介绍一下mutable关键字的作用
7.map的底层原理是什么?
8.map和unordered_map了解吗?
9.hashmap和map的区别,底层数据结构算法是什么?
10.介绍一下红黑树?
11.介绍一下hash
12.介绍一下二叉树?
13.编程实现LRU
14.C/C++中数组和链表的优缺点?
15.容器选择的原则
16.什么是迭代器,有哪几种迭代器
17.vector的底层原理和扩容机制是什么?
结论先行
STL 容器可按序列式/关联式/无序关联式三类理解,选型核心看:
- 随机访问多 →
vector; - 频繁插入删除 →
list; - 两端操作 →
deque; - 查找+有序 →
map/set(红黑树); - 查找+无序 →
unordered_map(哈希表); - 算法题常客
LRU→ 哈希表 + 双向链表 🎯
逐条说明
1. 常用容器功能汇总
| 容器 | 底层 | 特点 |
|---|---|---|
vector |
动态数组 | 随机访问快,尾部插入快 |
list |
双向链表 | 插入删除快,无随机访问 |
deque |
分段连续数组 | 头尾插入删除都快 |
stack/queue |
适配器 | 基于 deque 或 list |
set/map |
红黑树 | 有序,O(logn)O(logn) |
unordered_set/map |
哈希表 | 平均 O(1)O(1),无序 |
2. vector 优缺点
✅ 随机访问 O(1)O(1),尾部插入均摊 O(1)O(1),内存连续缓存友好
❌ 中间/头部插入慢,扩容有重新分配和拷贝开销
3. list 优缺点
✅ 任意位置插入删除 O(1)O(1)
❌ 无随机访问,内存不连续,缓存不友好
4. deque 优缺点
✅ 头尾插入删除 O(1)O(1),支持随机访问
❌ 中间插入慢,迭代器实现复杂
5. map & set 优缺点
✅ 自动排序,查找/插入/删除 O(logn)O(logn)
❌ 比哈希表慢,需要自定义比较器
6. mutable 关键字
允许在 const 成员函数中修改该成员,常用于缓存、计数器、互斥锁:
class A {
mutable int count = 0;
public:
void foo() const { ++count; } // ✅ OK
};
7. map 的底层原理
通常基于红黑树实现,键值按比较器有序排列,保证 O(logn)O(logn) 的查找、插入、删除。
8. map vs unordered_map
| 维度 | map |
unordered_map |
|---|---|---|
| 底层 | 红黑树 | 哈希表 |
| 有序性 | 有序 | 无序 |
| 查找 | O(logn)O(logn) | 平均 O(1)O(1),最坏 O(n)O(n) |
| 内存 | 较少 | 较多 |
9. hashmap vs map,底层数据结构
map:红黑树,有序,O(logn)O(logn)hashmap(unordered_map):哈希表 + 拉链/开放寻址,平均 O(1)O(1)
10. 红黑树
自平衡二叉搜索树,五大性质保证高度近似 lognlogn:
- 节点红或黑
- 根黑
- 叶子(NIL)黑
- 红节点的子节点黑
- 任意节点到叶子路径黑节点数相同
通过旋转 + 变色维持平衡。
11. 哈希(Hash)
通过哈希函数把键映射到数组下标,实现快速查找。
冲突解决方法:拉链法、开放寻址法、再哈希法等。
12. 二叉树
每个节点最多两个子节点的树结构。常见变体:
- 二叉搜索树(BST)
- 平衡二叉树(AVL)
- 红黑树
- 堆(完全二叉树)
13. 编程实现 LRU
核心:哈希表 + 双向链表,保证 O(1)O(1) 查找和删除:
class LRUCache {
int cap;
list<pair<int,int>> lst; // (key, value)
unordered_map<int, list<pair<int,int>>::iterator> mp;
public:
LRUCache(int capacity) : cap(capacity) {}
int get(int key) {
if (!mp.count(key)) return -1;
lst.splice(lst.begin(), lst, mp[key]); // 移到队头
return mp[key]->second;
}
void put(int key, int val) {
if (mp.count(key)) {
mp[key]->second = val;
lst.splice(lst.begin(), lst, mp[key]);
} else {
if (lst.size() == cap) {
mp.erase(lst.back().first);
lst.pop_back();
}
lst.emplace_front(key, val);
mp[key] = lst.begin();
}
}
};
14. 数组 vs 链表
| 维度 | 数组 | 链表 |
|---|---|---|
| 访问 | 随机 O(1)O(1) | 顺序 O(n)O(n) |
| 插入删除 | 中间 O(n)O(n) | O(1)O(1) |
| 内存 | 连续,缓存友好 | 不连续 |
| 扩容 | 需要重新分配 | 动态增长 |
15. 容器选择原则
- 需要索引/排序 →
vector/deque - 频繁插入删除中间元素 →
list - 键值查找且需有序 →
map/set - 键值查找只需快 →
unordered_map/set - 优先保证缓存友好 → 连续容器
16. 迭代器及种类
迭代器是访问容器元素的"泛型指针"。C++ 分 5 类:
| 迭代器 | 能力 |
|---|---|
| 输入迭代器 | 只读,单次遍历 |
| 输出迭代器 | 只写,单次遍历 |
| 前向迭代器 | 可多读,单向 |
| 双向迭代器 | 可双向移动(list) |
| 随机访问迭代器 | 可 +n、-n(vector) |
17. vector 底层原理和扩容机制
底层是一块连续动态数组。
size():当前元素数capacity():容量
扩容:当 size == capacity 时,重新申请更大内存(常见 1.5 倍或 2 倍),拷贝/移动元素,释放旧内存。
- GCC libstdc++:2 倍
- MSVC:1.5 倍
频繁扩容可先用
reserve预分配,减少拷贝开销 ⚡
整体联系
这 17 题串起了 STL 容器选型 → 底层数据结构 → 算法复杂度 → 经典应用(LRU):
容器接口
底层数据结构
时间/空间复杂度
场景化选型
LRU等经典实现

理解底层(数组、链表、红黑树、哈希表),才能真正做到用对容器、写出高效代码 🚀