2484 字
12 分钟
C++ STL 容器学习笔记

C++ STL 容器学习笔记#

学习日期: 2025-11-04
学习进度: vector, stack, queue, priority_queue
后续更新: map, set, unordered_map 等容器将持续补充


目录#

  1. vector - 动态数组
  2. stack - 栈
  3. queue - 队列
  4. priority_queue - 优先队列
  5. 性能对比与选择建议

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]; // 10
cout << v[1]; // 20
// at() 方法(带边界检查)
cout << v.at(2); // 30
// cout << v.at(10); // 抛出异常
// 首尾元素
cout << v.front(); // 10
cout << v.back(); // 30

3. 大小和容量#

vector<int> v = {1, 2, 3};
// 获取大小
cout << v.size(); // 3
// 检查是否为空
cout << v.empty(); // false (0)
// 清空 vector
v.clear(); // v.size() == 0
// 调整大小
v.resize(5); // 扩展到 5 个元素,新元素为 0
v.resize(10, -1); // 扩展到 10 个元素,新元素为 -1
v.resize(2); // 缩减到 2 个元素,多余部分被丢弃

二维 vector 使用示例#

// 示例:构建邻接表
vector<int> graph[100]; // 100 个节点的图
// 添加边:1 -> 2, 1 -> 3
graph[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 在容量不足时会:
    1. 分配新的更大内存
    2. 拷贝所有旧元素
    3. 释放旧内存
  • 频繁扩容导致 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("()[]{}"); // true
cout << isValid("([)]"); // false
cout << isValid("{[()]}"); // true

queue#

什么是 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;
// 二维网格的 BFS
int 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;
}

性能对比#

时间复杂度对比#

操作vectorstackqueuepriority_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
DFSstack / vector
BFSqueue
最值查询priority_queue

继续学习#

已完成: vector, stack, queue, priority_queue ✅

待学习:

  • map / multimap
  • set / multiset
  • unordered_map / unordered_set
  • deque
  • list

学习建议:

  1. 先掌握当前 4 个容器
  2. 刷 20+ 道相关题目巩固
  3. 再学习 map/set 系列

记住这些#

“vector 能做栈和数组,stack 只能做栈"
"BFS 用 queue,DFS 用 stack"
"需要动态最值,优先队列是王道"
"提前分配空间,避免 vector 频繁扩容”


推荐练习#

  1. LeetCode 20. 有效的括号 - stack
  2. LeetCode 102. 二叉树层序遍历 - queue
  3. LeetCode 215. 数组中的第K个最大元素 - priority_queue
  4. LeetCode 239. 滑动窗口最大值 - 综合应用

文档版本: v1.0 (持续更新中)
最后更新: 2025-11-04
学习进度: 40% (4/10 容器)

C++ STL 容器学习笔记
https://wuxie233.com/posts/cpp-stl-containers/
作者
WuXie
发布于
2025-11-04
许可协议
CC BY-NC-SA 4.0