STL 中最常用的容器——一个能自动伸缩的数组,支持下标快速访问,也能随时增删元素。
vector 是 STL 中最常用的容器,可以把它理解成一个能自动伸缩的数组。当你需要一个可以随时增减元素、且能用下标快速访问任意位置的序列时,vector 是第一选择。使用前需引入 <vector> 头文件。
| 1 | #include <iostream> |
| 2 | #include <vector> |
| 3 | using namespace std; |
| 4 | |
| 5 | int main() |
| 6 | { |
| 7 | vector<int> v1; // 空数组,里面什么都没有 |
| 8 | vector<int> v2(5); // 5 个元素,全部自动初始化为 0 |
| 9 | vector<int> v3(5, 7); // 5 个元素,全部初始化为 7 |
| 10 | vector<int> v4 = {1, 2, 3, 4, 5}; // 直接用花括号列出元素(最直观的写法) |
| 11 | vector<int> v5(v4); // 拷贝构造——复制一个一模一样的数组 |
| 12 | |
| 13 | // 二维数组:3 行 4 列,全部初始化为 0 |
| 14 | vector<vector<int>> matrix(3, vector<int>(4, 0)); |
| 15 | return 0; |
| 16 | } |
vector<int> v(n) 创建的 n 个元素会被自动初始化为 0,不会像局部数组那样留下"脏数据"。vector<int> v(n, val) 创建的 n 个元素会被初始化为指定的 val。{...} 是 C++11 引入的语法,简洁直观,优先使用。
📏 容量管理
| 1 | vector<int> v = {1, 2, 3}; |
| 2 | v.size(); // 3——当前有多少个元素 |
| 3 | v.capacity(); // 已分配的内存最多能装多少个(通常 ≥ size) |
| 4 | v.empty(); // false——判断是否为空 |
| 5 | v.clear(); // 清空所有元素,size 变成 0 |
| 6 | v.resize(5); // 把大小调整为 5,多出来的位置填 0 |
| 7 | v.resize(10, -1); // 把大小调整为 10,多出来的位置填 -1 |
| 8 | v.reserve(1000); // 提前预留 1000 个位置的空间 |
| 9 | v.shrink_to_fit(); // 把多余的内存释放掉 |
vector 扩容时,需要重新找一块更大的内存,然后把旧数据全部搬过去——这很花时间。如果你预先知道大概要存 1000 个元素,提前调用 reserve(1000) 就可以避免反复搬家,显著提升性能。这是竞赛中常见的优化手段。
clear() 只把元素清掉,让 size() 变成 0,但已经占用的内存不会释放。如果想真正释放内存,可以用 vector<int>().swap(v) 这个小技巧。
🔍 元素访问
| 1 | vector<int> v = {10, 20, 30}; |
| 2 | int x = v[0]; // 10——用下标访问,不检查越界 |
| 3 | int y = v.at(0); // 10——用 at 访问,越界时会报错 |
| 4 | int first = v.front(); // 10——第一个元素 |
| 5 | int last = v.back(); // 30——最后一个元素 |
| 6 | int* p = v.data(); // 获取底层数组的原始指针(需要调用 C 函数时有用) |
memcpy、scanf),这时可以用 v.data() 把 vector 当成普通数组来用。vector 在尾部增删元素是 O(1),非常快。但在头部或中间插入/删除是 O(n),因为需要把后面的元素全部挪位置。如果需要频繁在中间操作,应该考虑用 list。✏️ 增删修改
| 1 | vector<int> v = {1, 2, 3}; |
| 2 | v.push_back(4); // 在末尾追加:{1, 2, 3, 4} |
| 3 | v.pop_back(); // 删除最后一个:{1, 2, 3} |
| 4 | v.insert(v.begin() + 1, 99); // 在下标 1 处插入 99:{1, 99, 2, 3} |
| 5 | v.erase(v.begin() + 1); // 删除下标 1 的元素:{1, 2, 3} |
| 6 | v1.swap(v2); // 交换两个 vector 的全部内容(O(1),极快) |
| 7 | v.emplace_back(10, 'a'); // 在末尾原地构造元素(比 push_back 更高效) |
push_back 是先造好一个临时对象再拷贝进去,emplace_back 是直接在末尾原地构造,省去了拷贝这一步。对于简单的 int 差别不大,但对于复杂的类型(比如 pair、结构体),emplace_back 更高效。| 1 | // ❌ 错误写法——erase 之后迭代器已经"过期"了 |
| 2 | for (auto it = v.begin(); it != v.end(); ++it) |
| 3 | { |
| 4 | if (*it == target) v.erase(it); // 危险!it 已失效,++it 会出错 |
| 5 | } |
| 6 | |
| 7 | // ✅ 正确写法——利用 erase 返回的新迭代器 |
| 8 | for (auto it = v.begin(); it != v.end(); ) |
| 9 | { |
| 10 | if (*it == target) |
| 11 | it = v.erase(it); // erase 返回被删除元素的下一个位置 |
| 12 | else |
| 13 | ++it; |
| 14 | } |
++ 是未定义行为,轻则结果错误,重则程序崩溃。记住口诀:erase 返回新位置,用它接着往下走,不要再手动 ++it。| 1 | #include <iostream> |
| 2 | #include <vector> |
| 3 | using namespace std; |
| 4 | |
| 5 | int main() |
| 6 | { |
| 7 | vector<int> v = {1, 2, 3, 4, 5}; |
| 8 | |
| 9 | // 遍历方式一:下标 |
| 10 | for (size_t i = 0; i < v.size(); i++) { |
| 11 | cout << v[i] << " "; |
| 12 | } |
| 13 | cout << endl; |
| 14 | |
| 15 | // 遍历方式二:范围 for(最简洁) |
| 16 | for (int x : v) { |
| 17 | cout << x << " "; |
| 18 | } |
| 19 | cout << endl; |
| 20 | |
| 21 | // 删除偶数 |
| 22 | for (auto it = v.begin(); it != v.end(); ) { |
| 23 | if (*it % 2 == 0) |
| 24 | it = v.erase(it); |
| 25 | else |
| 26 | ++it; |
| 27 | } |
| 28 | // v 变成 {1, 3, 5} |
| 29 | |
| 30 | cout << "size: " << v.size() << endl; // 输出:3 |
| 31 | |
| 32 | return 0; |
| 33 | } |
把本节出现过的方法按用途归类汇总,写代码时可以直接当参考卡用。
| 方法 | 分类 | 作用 |
|---|---|---|
| v.size() / v.empty() | 容量 | 元素个数 / 判断是否为空 |
| v.capacity() | 容量 | 已分配的内存最多能装多少个 |
| v.clear() | 容量 | 清空元素(size 变 0,内存不释放) |
| v.resize(n) / v.resize(n, val) | 容量 | 调整大小,多出的位置填 0 或 val |
| v.reserve(n) | 容量 | 提前预留 n 个位置,避免反复扩容 |
| v.shrink_to_fit() | 容量 | 释放多余内存 |
| v[i] | 访问 | 下标访问,不检查越界,速度快 |
| v.at(i) | 访问 | 下标访问,越界抛异常,更安全 |
| v.front() / v.back() | 访问 | 第一个 / 最后一个元素 |
| v.data() | 访问 | 获取底层数组的原始指针 |
| v.push_back(x) / v.pop_back() | 修改 | 末尾追加 / 删除元素,O(1) |
| v.insert(it, x) | 修改 | 在 it 位置插入元素,O(n) |
| v.erase(it) | 修改 | 删除 it 位置的元素,返回下一个有效迭代器 |
| v.emplace_back(args...) | 修改 | 末尾原地构造元素,省去拷贝 |
| v1.swap(v2) | 修改 | 交换两个 vector 的全部内容,O(1) |
| v.begin() / v.end() | 遍历 | 正向迭代器范围 |