vector - 动态数组
std::vector 是算法竞赛中最常用的序列容器,提供O(1) 随机访问和均摊 O(1) 尾部插入。掌握 C++11 特性,能让你的代码更快更简洁,且在蓝桥杯考场上编译通过。
竞赛中的核心考点
| 操作 | 时间复杂度 | 竞赛要点 |
|---|---|---|
| 随机访问 | O(1) | 替代普通数组,支持动态扩容 |
| 尾部 push/pop | 均摊 O(1) | 用作栈 (stack) |
| 中间插入/删除 | O(n) | 避免在循环中频繁使用 |
reserve |
O(n) | 预处理避免 reallocation |
lower_bound |
O(log n) | 配合 sort 实现二分查找 |
一、初始化与构造
1.1 竞赛常用初始化
|
|
1.2 预分配空间 - 避免 TLE
|
|
二、emplace_back vs push_back
2.1 性能对比
|
|
2.2 竞赛建议
- 对于基本类型 (
int,long long):两者无差别 - 对于自定义结构体:使用
emplace_back减少开销 - 对于
pair和tuple:emplace_back可以直接传参数
|
|
三、遍历技巧
3.1 竞赛常用遍历方式
|
|
3.2 倒序遍历
|
|
四、算法配合 (C++11 写法)
4.1 C++11 排序与去重
|
|
4.2 C++11 二分查找
|
|
4.3 前缀和
|
|
五、C++11 安全删除法 (O(N) 性能)
5.1 为什么 erase + 循环是 O(N²) 陷阱
|
|
5.2 C++11 Erase-Remove 惯用法 (O(N) 性能秒杀)
|
|
5.3 多条件删除
|
|
六、vector 性能天坑
6.1 为什么不推荐使用 vector
|
|
6.2 正确替代方案
|
|
6.3 性能对比
| 类型 | 访问时间 | 内存 | 适用场景 |
|---|---|---|---|
vector<bool> |
最慢 | 1 bit | ❌ 不推荐 |
vector<char> |
最快 | 1 byte | ✅ 竞赛首选 |
vector<uint8_t> |
快 | 1 byte | ✅ 明确语义 |
vector<int> |
快 | 4 byte | 需要位运算时 |
七、迭代器失效问题
7.1 失效场景
|
|
7.2 安全删除元素
|
|
八、竞赛实战技巧
8.1 用 vector 实现栈
|
|
8.2 邻接表存图
|
|
8.3 动态规划中的滚动数组
|
|
8.4 离散化
|
|
九、常见错误与注意事项
| 错误 | 说明 | 解决方案 |
|---|---|---|
v[i] 越界 |
未检查 i < v.size() |
使用 at() 或先检查 |
| 迭代器失效 | push_back后迭代器失效 | 重新获取迭代器 |
| erase + 循环 | O(N²) 复杂度导致 TLE | 使用 Erase-Remove 惯用法 |
使用 vector<bool> |
常数巨大,无法返回引用 | 使用 vector<char> |
size() - 1 溢出 |
无符号减法溢出 | 使用 (int)v.size() - 1 |
未 reserve 导致 TLE |
频繁扩容 | 预先 reserve |
十、C++11 完整竞赛模板
|
|
总结:C++11 竞赛要点
- 删除元素:只用 Erase-Remove 惯用法 (O(N)),永远不要 erase + 循环
- 布尔数组:只用
vector<char>或vector<uint8_t> - 输入读入:使用
for (auto& x : v) cin >> x - 排序二分:使用
sort(all(v))和lower_bound(all(v), x) - 长度计算:使用
(int)v.size() - 1代替v.size() - 1 - 宏定义:使用
#define all(x) x.begin(), x.end()提高打字速度 - 原地构造:对于自定义类型,使用
emplace_back减少开销