一、vector 简介
1.1 什么是 vector?
vector 是 C++ STL(标准模板库)中的一种动态数组容器。
它本质上是一块连续的动态内存空间,可以:
-
自动扩容
-
随机访问
-
在尾部高效插入删除
-
保存任意类型的数据
例如:
#include <vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
return 0;
}
此时:
v
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
└───┴───┴───┘
vector 内部保存三个整数。
二、vector 的底层原理
理解 vector 最重要的是理解它的三个成员:
start
finish
end_of_storage
可以理解为:
capacity空间
start end_of_storage
↓ ↓
┌────┬────┬────┬────┬────┐
│ 10 │ 20 │ 30 │ │ │
└────┴────┴────┴────┴────┘
↑ ↑
finish 可扩容空间
三个指针:
| 成员 | 作用 |
|---|---|
| start | 指向数组开始 |
| finish | 指向有效元素末尾 |
| end_of_storage | 指向申请空间末尾 |
size 和 capacity 区别
这是 vector 高频考点。
size()
表示:
当前已经存储多少个元素
例如:
vector<int> v;
v.push_back(10);
v.push_back(20);
cout << v.size();
输出:
2
capacity()
表示:
当前申请了多少内存空间
例如:
vector<int> v;
v.push_back(10);
cout << v.capacity();
可能输出:
1
也可能:
2
因为不同编译器扩容策略不同。
例如:
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
可能:
size = 3
capacity = 4
内存:
capacity=4
┌───┬───┬───┬───┐
│ 1 │ 2 │ 3 │ │
└───┴───┴───┴───┘
三、vector 的初始化方式
3.1 默认初始化
vector<int> v;
创建空 vector:
size=0
capacity=0
3.2 指定大小
vector<int> v(5);
创建 5 个元素:
0 0 0 0 0
3.3 指定大小和值
vector<int> v(5,10);
结果:
10 10 10 10 10
3.4 使用数组初始化
int arr[]={1,2,3,4};
vector<int> v(arr,arr+4);
结果:
1 2 3 4
3.5 拷贝构造
vector<int> v1{1,2,3};
vector<int> v2(v1);
两个 vector 独立:
v1:
1 2 3
v2:
1 2 3
修改 v2:
v2[0]=100;
不会影响 v1。
因为 vector 使用:
深拷贝
四、vector 常用接口大全
4.1 push_back()
尾插元素
vector<int> v;
v.push_back(10);
v.push_back(20);
结果:
10 20
时间复杂度:
O(1)
平均情况下。
4.2 pop_back()
删除尾部元素
v.pop_back();
例如:
10 20 30
删除:
10 20
注意:
只能删除最后一个。
4.3 size()
获取元素个数
cout<<v.size();
返回:
size_t
4.4 empty()
判断是否为空
if(v.empty())
{
cout<<"空";
}
返回:
true / false
4.5 clear()
清空元素
v.clear();
例如:
之前:
1 2 3 4
之后:
空
注意:
clear 不一定释放空间。
例如:
vector<int> v(100);
v.clear();
cout<<v.capacity();
可能:
100
4.6 reserve()
提前开空间
vector<int> v;
v.reserve(100);
表示:
提前申请100个元素空间。
例如:
没有 reserve:
1
2
4
8
16
32
...
不断扩容。
使用 reserve:
capacity=100
减少扩容次数。
reserve 注意
vector<int> v;
v.reserve(100);
cout<<v.size();
输出:
0
因为:
reserve 只改变 capacity。
不会创建元素。
4.7 resize()
改变元素数量
例如:
vector<int> v;
v.resize(5);
结果:
0 0 0 0 0
指定值:
v.resize(5,10);
结果:
10 10 10 10 10
区别:
| 函数 | 作用 |
|---|---|
| reserve | 扩容空间 |
| resize | 改变元素数量 |
五、vector 的访问方式
5.1 operator\[\]
vector<int> v={1,2,3};
cout<<v[0];
输出:
1
特点:
不检查越界。
例如:
v[10];
可能崩溃。
5.2 at()
cout<<v.at(0);
特点:
检查越界。
例如:
v.at(10);
抛异常:
out_of_range
5.3 front()
访问第一个元素
cout<<v.front();
5.4 back()
访问最后一个元素
cout<<v.back();
六、vector 迭代器
迭代器是 STL 的核心。
vector 迭代器类似指针。
6.1 begin()
开始位置
vector<int>::iterator it=v.begin();
指向:
第一个元素
6.2 end()
结束位置
v.end();
注意:
end 指向最后一个元素后面。
例如:
1 2 3
^ ^
| |
begin end
遍历 vector
方法1:普通 for
for(size_t i=0;i<v.size();i++)
{
cout<<v[i];
}
方法2:迭代器
vector<int>::iterator it=v.begin();
while(it!=v.end())
{
cout<<*it<<" ";
it++;
}
方法3:范围 for
C++11:
for(auto e:v)
{
cout<<e;
}
七、vector 插入和删除
7.1 insert()
指定位置插入
例如:
vector<int> v={1,2,3};
v.insert(v.begin()+1,100);
结果:
1 100 2 3
时间复杂度:
O(N)
因为需要移动元素。
7.2 erase()
删除指定位置
v.erase(v.begin()+1);
例如:
1 2 3
删除2:
1 3
删除区间:
v.erase(v.begin(),v.begin()+2);
删除:
前两个元素
八、vector 扩容机制(重点)
当空间不足:
例如:
capacity=3
1 2 3
继续:
push_back(4);
发生:
-
申请更大的空间
-
拷贝旧数据
-
释放旧空间
-
插入新数据
过程:
旧空间
1 2 3
↓
新空间
1 2 3 _ _ _
↓
释放旧空间
为什么 vector 扩容不是一点一点增加?
因为:
如果每次增加1:
插入n个元素:
需要大量搬迁。
效率:
O(n²)
所以通常采用:
2倍扩容
达到:
均摊 O(1)
九、vector 的深浅拷贝
vector 内部:
保存:
指针
数据
容量
如果浅拷贝:
v1
↓
同一块空间
↑
v2
会造成:
-
修改互相影响
-
double free
所以 vector 使用:
深拷贝
例如:
vector<int> v1={1,2,3};
vector<int> v2=v1;
内存:
v1:
1 2 3
v2:
1 2 3
两个空间独立。
十、vector 失效问题(面试重点)
10.1 扩容导致迭代器失效
vector<int> v={1,2,3};
auto it=v.begin();
v.push_back(4);
cout<<*it;
可能错误。
原因:
扩容后:
旧空间释放。
原 iterator 指向:
已经释放的地址
10.2 erase导致迭代器失效
auto it=v.begin();
v.erase(it);
之后:
it 不可使用。
十一、vector 与数组区别
| 数组 | vector | |
|---|---|---|
| 大小 | 固定 | 动态 |
| 内存 | 栈区 | 堆区 |
| 扩容 | 不能 | 可以 |
| 效率 | 高 | 略低 |
| 安全性 | 低 | 高 |
十二、vector 与 list 区别
| vector | list | |
|---|---|---|
| 底层 | 连续数组 | 链表 |
| 随机访问 | 快 | 慢 |
| 插入删除 | 中间慢 | 快 |
| 缓存友好 | 好 | 差 |
十三、vector 性能分析
| 操作 | 复杂度 |
|---|---|
| 随机访问 | O(1) |
| 尾插 | O(1) |
| 尾删 | O(1) |
| 头插 | O(N) |
| 中间插入 | O(N) |
| 查找 | O(N) |
十四、vector 模拟实现(核心思想)
简单版本:
template<class T>
class Vector
{
public:
Vector()
{
_start=nullptr;
_finish=nullptr;
_end=nullptr;
}
private:
T* _start;
T* _finish;
T* _end;
};
三个指针:
_start
↓
[1][2][3][ ][ ]
↑
finish
↑
end
这就是 STL vector 的核心结构。
十五、vector 高频面试题
1.vector 为什么支持随机访问?
因为:
底层是连续数组。
可以:
地址=首地址+下标*sizeof(type)
2.vector 为什么尾插效率高?
因为:
尾部空间直接写入。
3.vector 扩容为什么消耗大?
因为:
需要:
-
申请新空间
-
拷贝数据
-
释放旧空间
4.reserve 和 resize区别?
答案:
reserve:
改变容量
resize:
改变元素数量
5.vector 如何释放空间?
方法:
vector<int>().swap(v);
原理:
创建临时空 vector
交换资源。
总结
vector 是 C++ STL 中最重要的容器之一。
核心知识:
-
vector 是动态数组
-
底层是一块连续空间
-
使用三个指针管理数据
-
size 表示元素数量
-
capacity 表示容量
-
扩容会导致迭代器失效
-
尾插效率高
-
中间插入删除效率低
-
支持随机访问
-
STL 中大量算法基于 vector