**📚 超贴心目录
1.🎬 开场白:为啥 vector 是新手必学?
2.🧱 基础打底:vector 是个啥?有啥超能力?
3✨ 核心技能:手把手教你用 vector(接口篇)
3.1 开局第一步:怎么创建 vector?(构造函数)
3.2 遍历神器:迭代器咋用不翻车?
3.3 空间管理:size 和 capacity 别再搞混了!
3.4 增删查改:新手常用操作全拆解
4.⚠️ 避坑指南:迭代器失效 = 程序崩溃?手把手教你躲坑!
5.🚀 实战练兵:OJ 题用 vector 秒解(新手能看懂的解法)
6.🧐 刨根问底:vector 底层到底是个啥?(新手版)
7.📝 通关总结:核心知识点 + 复习口诀
1.🎬 开场白:为啥 vector 是新手必学?
作为 C++ 新手,你是不是刚学完数组就被 "数组大小固定、越界就崩溃" 折磨得头疼?是不是想找一个 "能自动变大变小、用起来像数组一样简单" 的容器?
**那 vector 就是你的 "救星"!**它是 C++ STL 里最基础、最常用的动态数组,学会它不仅能搞定日常编程,还能轻松应对 OJ 刷题、课程设计,甚至是入门级面试 ------ 可以说,vector 是新手从 "只会写数组" 到 "能用 STL" 的第一个关键台阶。
🧱 基础打底:vector 是个啥?有啥超能力?
2.1 先讲明白:vector 的本质
你可以把 vector 理解成 "智能版的数组":
- 像普通数组一样,支持[]下标访问(比如v[0]取第一个元素),随机访问速度贼快;
- 比数组牛的是:它能 "自动长大"------ 你往里面加元素,它会自己扩容,不用你手动申请内存;
- 但它也有小缺点:如果在中间插 / 删元素,后面的元素要 "挪位置",效率会低一点(新手先记住 "尾插尾删最香" 就行)。
2.2 必知:vector 的扩容小秘密
新手不用深究底层,但要记住两个关键:
- 扩容不是 "加一个元素扩一次":vector 会提前多申请一些空间(叫 "备用空间"),只有当实际元素个数(size)等于总空间(capacity)时,才会触发扩容;
- 扩容倍数有讲究:
VS 编译器(新手常用):扩到原来的1.5 倍 (比如原来能存 10 个,扩容后能存 15 个);
G++ 编译器:扩到原来的2 倍(原来 10 个,扩容后 20 个); - 扩容有 "小代价":扩容时会先开一块新空间,把旧元素搬过去,再删掉旧空间 ------ 所以提前知道要存多少元素的话,能手动预留空间,避免频繁扩容(后面会讲)。
✨ 核心技能:手把手教你用 vector(接口篇)
3.1 开局第一步:怎么创建 vector?(构造函数)
先掌握 4 种最常用的创建方式,:
cpp
#include <iostream>
#include <vector> // 用vector必须加这个头文件!新手别忘
using namespace std; // 新手先加这个,避免写std::vector
int main() {
// 方式1:空vector(最常用!先创建,后面再加元素)
vector<int> v1;
// 新手提示:int可以换成double、string,比如vector<string> v_str;
// 方式2:创建时直接初始化n个相同元素(比如5个3)
vector<int> v2(5, 3);
cout << "v2的元素:";
for (int i = 0; i < v2.size(); i++) {
cout << v2[i] << " "; // 输出:3 3 3 3 3
}
cout << endl;
// 方式3:用已有的vector拷贝(复制一份一模一样的)
vector<int> v3(v2);
cout << "v3的元素和v2一样:";
for (int e : v3) { // 新手也可以学这种范围for,更简单
cout << e << " ";
}
cout << endl;
// 方式4:用数组初始化(新手常把数组转vector用)
int a[] = {1,2,3,4};
vector<int> v4(a, a+4); // 从a[0]到a[3],共4个元素
cout << "v4的元素:";
for (auto e : v4) { // auto新手可以理解成"让编译器自己判断类型"
cout << e << " "; // 输出:1 2 3 4
}
cout << endl;
return 0;
}
小提醒:
- 创建 vector 时要指定类型(比如vector),不能直接写vector;
- 方式 1 是新手最常用的,因为很多时候一开始不知道要存多少元素。
3.2 遍历神器:迭代器咋用不翻车?
新手可能觉得 "迭代器" 听起来高级,其实它就是 "能遍历 vector 的指针替代品",核心记住两种遍历方式就行:
cpp
vector<int> v{1,2,3,4}; // 新手注意:这种初始化方式C++11及以上支持
// 迭代器的写法:vector<类型>::iterator
vector<int>::iterator it = v.begin(); // begin指向第一个元素
while (it != v.end()) { // end指向最后一个元素的下一个位置(不是最后一个!)
cout << *it << " "; // *it取迭代器指向的元素,像指针一样
++it; // 迭代器往后走一步
}
// 输出:1 2 3 4
3.2.2 反向迭代器(反向遍历)
cpp
vector<int> v{1,2,3,4};
// 反向迭代器写法:vector<类型>::reverse_iterator
vector<int>::reverse_iterator it = v.rbegin(); // rbegin指向最后一个元素
while (it != v.rend()) { // rend指向第一个元素的前一个位置
cout << *it << " ";
++it; // 反向迭代器++是往前跑
}
// 输出:4 3 2 1
避坑点:
- end()和rend()都不指向有效元素,千万别解引用(比如*v.end()会崩溃);
- 新手如果觉得迭代器麻烦,先用for(int i=0; i<v.size(); i++)或范围 for(for(auto e : v)),足够应付大部分场景。
3.3 空间管理:size 和 capacity 别再搞混了!
新手最容易把 "实际元素个数" 和 "总空间大小" 搞混,用表格和代码帮你彻底分清:

cpp
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v;
// 先看初始状态:空vector
cout << "初始size:" << v.size() << endl; // 0
cout << "初始capacity:" << v.capacity() << endl; // VS下是0,G++下可能是0/1
cout << "是否为空:" << boolalpha << v.empty() << endl; // true(boolalpha让输出是true/false)
// 尾插5个元素
for (int i = 0; i < 5; i++) {
v.push_back(i);
}
cout << "插5个元素后size:" << v.size() << endl; // 5
cout << "插5个元素后capacity:" << v.capacity() << endl; // VS下是8,G++下是8(2倍扩容)
// resize:改元素个数
v.resize(8, 0); // 改成8个元素,新增的3个填0
cout << "resize后size:" << v.size() << endl; // 8
cout << "resize后capacity:" << v.capacity() << endl; // 还是8(没超容量)
cout << "第6个元素:" << v[5] << endl; // 0(新增的元素)
// reserve:预留空间
v.reserve(10);
cout << "reserve后size:" << v.size() << endl; // 还是8(只开空间,不填元素)
cout << "reserve后capacity:" << v.capacity() << endl; // 10
return 0;
}
核心技巧:
- 如果提前知道要存 100 个元素,先v.reserve(100),避免插入时频繁扩容(新手写循环插元素时,加这一行能让代码快很多);
- resize会改变能访问的元素个数(比如 resize (8) 后,v [7] 是有效的),reserve不会(reserve (10) 后,v [8] 还是越界)。
3.4 增删查改 常用操作全拆解
新手用 vector,80% 的操作都是 "尾插、尾删、查元素、改元素",直接上代码 + 新手注释:
cpp
#include <vector>
#include <iostream>
#include <algorithm> // find函数需要这个头文件!新手别忘
using namespace std;
int main() {
vector<int> v{1,2,3,4};
// 1. 尾插(最常用!新手优先用这个)
v.push_back(5);
cout << "尾插5后:";
for (auto e : v) cout << e << " "; // 1 2 3 4 5
cout << endl;
// 2. 尾删(也常用,删最后一个元素)
v.pop_back();
cout << "尾删后:";
for (auto e : v) cout << e << " "; // 1 2 3 4
cout << endl;
// 3. 查找元素(新手注意:vector没有自带find,要用算法库的)
// find(起始迭代器, 结束迭代器, 要找的值)
auto pos = find(v.begin(), v.end(), 3);
if (pos != v.end()) { // 找到才操作!新手别漏这个判断
cout << "找到3啦,位置是:" << pos - v.begin() << endl; // 位置2(从0数)
} else {
cout << "没找到3" << endl;
}
// 4. 插入元素(新手尽量少用中间插入,效率低)
if (pos != v.end()) {
v.insert(pos, 10); // 在3的前面插10
cout << "插入10后:";
for (auto e : v) cout << e << " "; // 1 2 10 3 4
cout << endl;
}
// 5. 删除元素(新手注意:erase后迭代器会失效,要重新赋值)
pos = find(v.begin(), v.end(), 10); // 重新找10的位置
if (pos != v.end()) {
v.erase(pos); // 删除10
cout << "删除10后:";
for (auto e : v) cout << e << " "; // 1 2 3 4
cout << endl;
}
// 6. 改元素(直接用[],和数组一样)
v[2] = 99; // 把第三个元素(位置2)改成99
cout << "修改后第三个元素:" << v[2] << endl; // 99
// 7. 清空所有元素(新手常用)
v.clear();
cout << "清空后size:" << v.size() << endl; // 0
cout << "清空后capacity:" << v.capacity() << endl; // 还是原来的大小(只删元素,不缩空间)
return 0;
}
小口诀:
- 插删优先用 "尾插尾删"(push_back/pop_back),中间插删尽量少用;
- 查找元素要加 "是否找到" 的判断,不然会操作无效迭代器;
- 清空用 clear (),但 clear 只删元素,不释放空间(新手暂时不用管释放,编译器会自动处理)。
4.⚠️ 避坑指南:迭代器失效 = 程序崩溃?手把手教你躲坑!
迭代器失效是新手最容易踩的坑,比如写着写着程序突然崩溃,大概率是这个原因!先给新手讲明白 "为啥失效",再教 "怎么躲坑"。
4.1 新手能懂的 "迭代器失效" 定义
迭代器本质是 "指向 vector 底层数组的指针",如果这个指针指向的空间被销毁了,或者指向的位置不对了,再用这个迭代器,就会 "失效"------ 好比你记了朋友家的地址,结果朋友搬家了,你还按旧地址找,肯定找不到(程序就崩溃)。
4.2 两种常见失效场景(必记)
场景 1:扩容导致 "全局失效"(所有迭代器都不能用了)
只要 vector 扩容(比如 push_back、reserve、resize、insert 导致容量不够),底层会开新空间、删旧空间,原来的迭代器就指向了 "被
删掉的旧空间",再用就崩溃。
错误示例
cpp
vector<int> v{1,2,3,4};
auto it = v.begin(); // 记录第一个元素的迭代器
v.reserve(100); // 扩容,旧空间被删
cout << *it << endl; // 崩溃!it指向的空间没了
正确示例
cpp
vector<int> v{1,2,3,4};
auto it = v.begin();
v.reserve(100);
it = v.begin(); // 扩容后重新给迭代器赋值!
cout << *it << endl; // 正常输出1
场景 2:erase 导致 "局部失效"(被删位置的迭代器不能用)
erase 删除元素后,后面的元素会往前挪,被删位置的迭代器就 "空了",如果直接 ++ 会越界。
错误示例
cpp
vector<int> v{1,2,3,4};
auto it = v.begin();
while (it != v.end()) {
if (*it % 2 == 0) {
v.erase(it); // 删除后it失效了!
}
++it; // 失效的it++,直接崩溃
}
正确示例
cpp
vector<int> v{1,2,3,4};
auto it = v.begin();
while (it != v.end()) {
if (*it % 2 == 0) {
it = v.erase(it); // erase返回下一个有效迭代器,重新赋值!
} else {
++it; // 没删除才++
}
}
// 最终v:{1,3},正常运行
4.3 避坑总结
- 扩容后:所有迭代器重新赋值;
- erase 后:用 erase 的返回值更新迭代器;
- 如果怕麻烦,扩容 /erase 后直接重新遍历(比如用 for 循环),别复用之前的迭代器。
🚀 实战练兵:OJ 题用 vector 秒解(新手能看懂的解法)
学会了基础,就得刷题巩固!选两道新手能独立完成的经典 OJ 题,用 vector 实现
5.1 题目 1:只出现一次的数字(简单题)
题目描述 :给定一个非空整数数组,除了一个元素只出现一次,其余每个元素都出现两次,找出这个只出现一次的元素。
思路:不用复杂算法,先排序,再遍历找 "只出现一次" 的元素(新手能理解的解法)。
cpp
#include <vector>
#include <iostream>
#include <algorithm> // sort需要这个头文件
using namespace std;
int main() {
// 新手输入:先输入数组长度,再输入元素
int n;
cout << "请输入数组长度:";
cin >> n;
vector<int> nums(n); // 创建n个元素的vector
cout << "请输入" << n << "个整数:";
for (int i = 0; i < n; i++) {
cin >> nums[i]; // 输入元素
}
// 新手解法:排序后遍历
sort(nums.begin(), nums.end()); // 排序,相同元素会挨在一起
int res = nums[0]; // 初始化结果
for (int i = 1; i < nums.size(); i++) {
if (nums[i] == nums[i-1]) {
// 相同的话,跳过下一个元素
i++;
} else {
res = nums[i];
}
}
cout << "只出现一次的数字是:" << res << endl;
return 0;
}
提示:
- sort 排序后,相同元素会相邻,只要找到 "和前一个不一样" 的元素就行;
- 这道题还有更高效的异或解法(前面提过),新手先掌握这种 "直观解法",后续再学优化。
5.2 题目 2:杨辉三角(简单题)
题目描述:给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
思路:
- 杨辉三角每行第一个和最后一个元素是 1;
- 中间元素 = 上一行的 "前一个元素 + 当前位置元素";
- 用二维 vector 存储,每行先 resize,再填值。
cpp
#include <vector>
#include <iostream>
using namespace std;
int main() {
int numRows;
cout << "请输入杨辉三角的行数:";
cin >> numRows;
// 二维vector:外层存行,内层存每行的元素
vector<vector<int>> yanghui(numRows);
// 新手分步写:先填每行的元素
for (int i = 0; i < numRows; i++) {
// 第i行有i+1个元素,resize初始化全为1
yanghui[i].resize(i + 1, 1);
// 中间元素(从第2行开始才有)
if (i >= 2) { // 第0行、第1行不用改,全是1
for (int j = 1; j < i; j++) { // j从1到i-1(跳过首尾)
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
}
}
}
// 输出杨辉三角(新手美化输出)
cout << "杨辉三角前" << numRows << "行:" << endl;
for (int i = 0; i < numRows; i++) {
// 先输空格,让三角更美观(新手可选)
for (int k = 0; k < numRows - i - 1; k++) {
cout << " ";
}
// 输出每行元素
for (int j = 0; j < yanghui[i].size(); j++) {
cout << yanghui[i][j] << " ";
}
cout << endl;
}
return 0;
}
收获:
- 二维 vector 就是 "vector 里面套 vector",可以理解成 "行数可变的二维数组";
- resize 不仅能改大小,还能填初始值,新手用这个能少写很多循环。
6.🧐 刨根问底:vector 底层到底是个啥?(新手版)
新手不用深入源码,但要知道 vector 的底层核心,能帮你理解 "为啥扩容、为啥迭代器失效":
6.1 核心结构:三个指针
vector 底层靠三个指针管理一块连续的数组:
- _start:指向数组第一个元素(新手理解成 "数组名" 就行);
- _finish:指向最后一个有效元素的下一个位置(所以 size = _finish - _start);
- _end_of_storage:指向数组最后一个空间的下一个位置(所以 capacity = _end_of_storage - _start)。
6.2 能懂的 "扩容过程"
当 push_back 元素,发现_finish == _end_of_storage(容量满了),就会:
1.开一块新空间(大小是原来的 1.5/2 倍);
2.把旧空间的元素 "搬" 到新空间(新手理解成 "复制过去");
3.删掉旧空间,把三个指针指向新空间;
4.把新元素插入到新空间的末尾。
6.3 别踩坑:memcpy 浅拷贝坑(简单提)
新手暂时不用写 vector 的底层,但要知道:如果 vector 存的是 string、自定义类这些 "带资源" 的类型,不能用 memcpy 复制(会导致重复释放内存),要用 "逐个拷贝"------ 这也是为啥 STL 的 vector 能安全存各种类型的原因。
📝 通关总结:核心知识点 + 复习口诀
7.1 核心知识点(新手记这 5 条就够)
- 本质:动态连续数组,随机访问快,尾插尾删快,中间插删慢;
- 扩容:VS1.5 倍、G++2 倍,reserve 提前预留空间能提速;
- 接口:push_back/pop_back(尾插尾删)、size/capacity(大小 / 容量)、resize/reserve(改大小 / 预留空间)、erase/insert(删 / 插,注意迭代器失效);
- 迭代器失效:扩容后全局失效、erase 后局部失效,解决方法是 "重新赋值迭代器";
- 二维 vector:数组的数组,每行独立,能动态调整大小。
7.2 复习口诀
扩容要 reserve,改数用 resize;
插删优先尾,迭代器要复位;
size 是个数,capacity 是仓库;
遍历用 for,新手不迷路。