2484 字
12 分钟
C++ STL 容器学习笔记
C++ STL 容器学习笔记
学习日期: 2025-11-04
学习进度: vector, stack, queue, priority_queue
后续更新: map, set, unordered_map 等容器将持续补充
目录
vector
什么是 vector?
vector 是 C++ STL 中的动态数组,可以自动调整大小。
特点:
- ✅ 支持随机访问(O(1))
- ✅ 尾部插入删除快(O(1) 均摊)
- ❌ 中间插入删除慢(O(n))
- ❌ 扩容时有性能开销
基本使用
#include <iostream>#include <vector>using namespace std;
int main() { // 1. 定义一维 vector vector<int> arr;
// 2. 初始化 100 个元素(提前分配,避免扩容) vector<int> arr1(100);
// 3. 初始化 100 个元素,初值为 1 vector<int> arr2(100, 1);
// 4. 定义二维 vector(100 行,列数不定) vector<vector<int>> arr3(100, vector<int>());
// 5. 定义二维 vector(100 行 666 列,初值 -1) vector<vector<int>> arr4(100, vector<int>(666, -1));
// 6. 固定行数的二维数组(推荐简洁写法) vector<int> arr5[100]; // 100 行,每行是独立的 vector
return 0;}常用成员函数
1. 添加和删除元素
vector<int> v;
// 尾部添加元素v.push_back(10); // [10]v.push_back(20); // [10, 20]v.push_back(30); // [10, 20, 30]
// 尾部删除元素v.pop_back(); // [10, 20]2. 访问元素
vector<int> v = {10, 20, 30};
// 下标访问cout << v[0]; // 10cout << v[1]; // 20
// at() 方法(带边界检查)cout << v.at(2); // 30// cout << v.at(10); // 抛出异常
// 首尾元素cout << v.front(); // 10cout << v.back(); // 303. 大小和容量
vector<int> v = {1, 2, 3};
// 获取大小cout << v.size(); // 3
// 检查是否为空cout << v.empty(); // false (0)
// 清空 vectorv.clear(); // v.size() == 0
// 调整大小v.resize(5); // 扩展到 5 个元素,新元素为 0v.resize(10, -1); // 扩展到 10 个元素,新元素为 -1v.resize(2); // 缩减到 2 个元素,多余部分被丢弃二维 vector 使用示例
// 示例:构建邻接表vector<int> graph[100]; // 100 个节点的图
// 添加边:1 -> 2, 1 -> 3graph[1].push_back(2);graph[1].push_back(3);
// 遍历节点 1 的所有邻居for (int neighbor : graph[1]) { cout << neighbor << " "; // 输出: 2 3}
// 访问第 1 行第 0 列cout << graph[1][0]; // 输出: 2注意事项
⚠️ 1. 提前指定长度,避免频繁扩容
// 不推荐:频繁扩容vector<int> v;for (int i = 0; i < 1000000; i++) { v.push_back(i); // 可能触发多次内存重分配}
// 推荐:提前预留空间vector<int> v;v.reserve(1000000); // 预留容量for (int i = 0; i < 1000000; i++) { v.push_back(i); // 不会触发扩容}
// 或者直接指定大小vector<int> v(1000000);for (int i = 0; i < 1000000; i++) { v[i] = i; // 直接赋值,最快}为什么?
push_back在容量不足时会:- 分配新的更大内存
- 拷贝所有旧元素
- 释放旧内存
- 频繁扩容导致 O(n²) 的时间复杂度!
⚠️ 2. size_t 类型溢出
vector<int> v = {1, 2, 3};
// 错误:size_t 是无符号类型,结果可能溢出for (int i = v.size() - 1; i >= 0; i--) { // 危险! cout << v[i];}
// 正确方法 1:使用 int 强制转换for (int i = (int)v.size() - 1; i >= 0; i--) { cout << v[i];}
// 正确方法 2:使用迭代器反向遍历for (auto it = v.rbegin(); it != v.rend(); ++it) { cout << *it;}原因:
v.size()返回size_t类型(无符号)- 32 位编译器:范围
[0, 2^32-1] - 64 位编译器:范围
[0, 2^64-1] v.size() - 1在空 vector 上会溢出成超大正数!
⚠️ 3. 二维 vector 的行列访问
// 方法 1:固定行数(推荐)vector<int> arr[100]; // 100 行,列数动态arr[1].push_back(233);cout << arr[1][0]; // 233
// 方法 2:动态行列(更灵活)vector<vector<int>> arr(100);arr[1].push_back(233);cout << arr[1][0]; // 233完整示例
#include <iostream>#include <vector>using namespace std;
int main() { // 创建并初始化 vector<int> scores(5, 0);
// 输入 5 个分数 for (int i = 0; i < 5; i++) { cin >> scores[i]; }
// 计算平均分 int sum = 0; for (int score : scores) { sum += score; } double avg = sum / (double)scores.size();
cout << "平均分: " << avg << endl;
return 0;}stack
什么是 stack?
stack 是后进先出(LIFO)的数据结构,就像一摞盘子。
特点:
- ✅ 只能访问栈顶元素
- ✅ 插入删除都在栈顶,O(1)
- ❌ 不能访问内部元素
应用场景:
- 函数调用栈
- 括号匹配
- 深度优先搜索(DFS)
- 表达式求值
基本使用
#include <iostream>#include <stack>using namespace std;
int main() { // 构造栈 stack<int> stk;
// 进栈 stk.push(10); // [10] stk.push(20); // [10, 20] stk.push(30); // [10, 20, 30]
// 取栈顶元素 cout << stk.top(); // 30
// 出栈 stk.pop(); // [10, 20] cout << stk.top(); // 20
// 检查是否为空 if (!stk.empty()) { cout << "栈非空" << endl; }
// 获取大小 cout << "栈大小: " << stk.size(); // 2
return 0;}常用成员函数
| 函数 | 功能 | 时间复杂度 |
|---|---|---|
push(x) | 进栈 | O(1) |
pop() | 出栈 | O(1) |
top() | 取栈顶 | O(1) |
empty() | 是否为空 | O(1) |
size() | 栈大小 | O(1) |
vector 当作 stack 使用
vector<int> v;
// 进栈v.push_back(10);v.push_back(20);v.push_back(30);
// 取栈顶cout << v.back(); // 30
// 出栈v.pop_back();cout << v.back(); // 20优势:
- ✅ 可以随机访问任意元素
- ✅ 更灵活
劣势:
- ❌ 代码语义不清晰
注意事项
⚠️ 1. 不可直接访问内部元素
stack<int> stk;stk.push(10);stk.push(20);
// 错误:stack 没有 [] 运算符// cout << stk[0]; // 编译错误
// 只能通过 top() 访问栈顶cout << stk.top(); // 20⚠️ 2. 必须用临时变量存储栈顶值
stack<int> stk;stk.push(100);
// 错误:pop() 不返回值// int val = stk.pop(); // 编译错误
// 正确:先取值再出栈int val = stk.top();stk.pop();cout << val; // 100实战示例:括号匹配
bool isValid(string s) { stack<char> stk;
for (char c : s) { if (c == '(' || c == '[' || c == '{') { stk.push(c); } else { if (stk.empty()) return false;
char top = stk.top(); stk.pop();
if ((c == ')' && top != '(') || (c == ']' && top != '[') || (c == '}' && top != '{')) { return false; } } }
return stk.empty();}
// 测试cout << isValid("()[]{}"); // truecout << isValid("([)]"); // falsecout << isValid("{[()]}"); // truequeue
什么是 queue?
queue 是先进先出(FIFO)的数据结构,就像排队买票。
特点:
- ✅ 队尾插入,队首删除
- ✅ 插入删除都是 O(1)
- ❌ 不能访问中间元素
应用场景:
- 广度优先搜索(BFS)
- 任务调度
- 缓冲区
基本使用
#include <iostream>#include <queue>using namespace std;
int main() { // 构造队列 queue<int> que;
// 进队 que.push(10); // [10] que.push(20); // [10, 20] que.push(30); // [10, 20, 30]
// 取队首 cout << que.front(); // 10
// 取队尾 cout << que.back(); // 30
// 出队 que.pop(); // [20, 30] cout << que.front(); // 20
// 大小 cout << que.size(); // 2
return 0;}常用成员函数
| 函数 | 功能 | 时间复杂度 |
|---|---|---|
push(x) | 进队(队尾) | O(1) |
pop() | 出队(队首) | O(1) |
front() | 取队首 | O(1) |
back() | 取队尾 | O(1) |
empty() | 是否为空 | O(1) |
size() | 队列大小 | O(1) |
注意事项
⚠️ 需要临时变量辅助
queue<int> que;que.push(100);
// 错误:pop() 不返回值// int val = que.pop();
// 正确:先取值再出队int val = que.front();que.pop();cout << val; // 100实战示例:BFS 最短路径
#include <iostream>#include <queue>#include <vector>using namespace std;
// 二维网格的 BFSint bfs(vector<vector<int>>& grid, int startX, int startY) { int n = grid.size(); int m = grid[0].size();
queue<pair<int, int>> que; que.push({startX, startY});
vector<vector<bool>> visited(n, vector<bool>(m, false)); visited[startX][startY] = true;
int steps = 0;
while (!que.empty()) { int size = que.size();
for (int i = 0; i < size; i++) { auto [x, y] = que.front(); que.pop();
if (grid[x][y] == 9) { // 找到目标 return steps; }
// 四个方向 int dx[] = {-1, 1, 0, 0}; int dy[] = {0, 0, -1, 1};
for (int d = 0; d < 4; d++) { int nx = x + dx[d]; int ny = y + dy[d];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny] && grid[nx][ny] != -1) { que.push({nx, ny}); visited[nx][ny] = true; } } } steps++; }
return -1; // 未找到}priority_queue
什么是 priority_queue?
priority_queue 是优先队列,自动维护元素的有序性,底层是二叉堆。
特点:
- ✅ 插入元素 O(log n)
- ✅ 取出最大/最小元素 O(1)
- ✅ 删除堆顶 O(log n)
- ❌ 只能访问堆顶
应用场景:
- Dijkstra 最短路径
- 哈夫曼编码
- 任务优先级调度
- 动态中位数
基本使用
#include <iostream>#include <queue>using namespace std;
int main() { // 1. 大顶堆(默认) priority_queue<int> pque1;
// 2. 小顶堆 priority_queue<int, vector<int>, greater<int>> pque2;
// 进堆 pque1.push(30); pque1.push(10); pque1.push(50); pque1.push(20);
// 取堆顶(最大值) cout << pque1.top(); // 50
// 出堆 pque1.pop(); cout << pque1.top(); // 30
return 0;}大顶堆 vs 小顶堆
// 大顶堆:堆顶是最大值priority_queue<int> maxHeap;maxHeap.push(5);maxHeap.push(2);maxHeap.push(8);cout << maxHeap.top(); // 8
// 小顶堆:堆顶是最小值priority_queue<int, vector<int>, greater<int>> minHeap;minHeap.push(5);minHeap.push(2);minHeap.push(8);cout << minHeap.top(); // 2常用成员函数
| 函数 | 功能 | 时间复杂度 |
|---|---|---|
push(x) | 进堆 | O(log n) |
pop() | 出堆 | O(log n) |
top() | 取堆顶 | O(1) |
empty() | 是否为空 | O(1) |
size() | 堆大小 | O(1) |
注意事项
⚠️ 1. 只能读堆顶,无法访问其他元素
priority_queue<int> pque;pque.push(10);pque.push(20);
// 错误:无法用下标访问// cout << pque[1]; // 编译错误
// 只能访问堆顶cout << pque.top(); // 20⚠️ 2. 堆内元素不可修改
// 错误:不能直接修改堆顶// pque.top() = 100; // 编译错误
// 正确:先取出,修改,再放回int val = pque.top();pque.pop();pque.push(val + 1);实战示例:前 K 个最大元素
vector<int> topKFrequent(vector<int>& nums, int k) { // 小顶堆,保持大小为 k priority_queue<int, vector<int>, greater<int>> pque;
for (int num : nums) { pque.push(num);
if (pque.size() > k) { pque.pop(); // 弹出最小值 } }
// 堆中剩下的就是前 k 大元素 vector<int> result; while (!pque.empty()) { result.push_back(pque.top()); pque.pop(); }
return result;}性能对比
时间复杂度对比
| 操作 | vector | stack | queue | priority_queue |
|---|---|---|---|---|
| 插入 | O(1) 均摊 | O(1) | O(1) | O(log n) |
| 删除 | O(1) 尾部 | O(1) | O(1) | O(log n) |
| 访问 | O(1) | O(1) 栈顶 | O(1) 队首/尾 | O(1) 堆顶 |
| 查找 | O(n) | - | - | - |
选择建议
| 需求 | 推荐容器 |
|---|---|
| 随机访问 | vector |
| 后进先出 | stack |
| 先进先出 | queue |
| 动态有序 | priority_queue |
| DFS | stack / vector |
| BFS | queue |
| 最值查询 | priority_queue |
继续学习
已完成: vector, stack, queue, priority_queue ✅
待学习:
-
map/multimap -
set/multiset -
unordered_map/unordered_set -
deque -
list
学习建议:
- 先掌握当前 4 个容器
- 刷 20+ 道相关题目巩固
- 再学习 map/set 系列
记住这些
“vector 能做栈和数组,stack 只能做栈"
"BFS 用 queue,DFS 用 stack"
"需要动态最值,优先队列是王道"
"提前分配空间,避免 vector 频繁扩容”
推荐练习
- LeetCode 20. 有效的括号 - stack
- LeetCode 102. 二叉树层序遍历 - queue
- LeetCode 215. 数组中的第K个最大元素 - priority_queue
- LeetCode 239. 滑动窗口最大值 - 综合应用
文档版本: v1.0 (持续更新中)
最后更新: 2025-11-04
学习进度: 40% (4/10 容器)
C++ STL 容器学习笔记
https://wuxie233.com/posts/cpp-stl-containers/