Appearance
vector容器详解
vector(动态数组)是STL中最常用的序列式容器,底层基于连续的内存空间实现,支持随机访问,可动态调整大小,兼顾了数组的高效访问和链表的灵活扩容,是日常开发和算法题中使用频率最高的容器之一,与关联式容器(set、map)形成互补。
一、vector核心特性
- 存储结构:连续内存空间,与普通数组类似,但支持动态扩容(自动分配更大的内存,拷贝原有数据);
- 访问效率:支持随机访问(通过下标访问),时间复杂度O(1),远高于list、set等容器;
- 插入删除:尾部插入/删除效率极高(O(1)),中间插入/删除效率较低(需移动后续元素,O(n));
- 灵活性:可动态调整大小,无需手动管理内存,支持各种算法(排序、查找等)。
vector与set、map的核心区别:vector是「序列式容器」,按插入顺序存储,不自动去重、不排序;set、map是「关联式容器」,按关键字存储,支持去重、排序和快速查找。
二、vector基础用法(完整代码)
cpp
#include <iostream>
#include <vector> // vector的头文件
#include <algorithm> // 用于排序等算法
using namespace std;
int main() {
// 1. 定义vector(多种方式)
vector<int> v1; // 空vector,默认初始容量
vector<int> v2(5, 0); // 容量为5,所有元素初始化为0
vector<int> v3(v2.begin(), v2.end()); // 拷贝v2的所有元素
vector<int> v4 = {1, 2, 3, 4, 5}; // 初始化列表(C++11及以上)
// 2. 插入元素
v1.push_back(1); // 尾部插入(推荐,效率高)
v1.push_back(2);
v1.insert(v1.begin() + 1, 3); // 中间插入(在索引1的位置插入3)
// 此时v1:[1, 3, 2]
// 3. 访问元素(三种方式)
cout << "通过下标访问v1[1]:" << v1[1] << endl; // 输出:3(无越界检查)
cout << "通过at访问v1[2]:" << v1.at(2) << endl; // 输出:2(有越界检查,越界抛异常)
cout << "通过迭代器访问:";
for (auto it = v1.begin(); it != v1.end(); it++) {
cout << *it << " ";
}
cout << endl; // 输出:1 3 2
// 4. 修改元素
v1[0] = 10; // 下标修改
v1.at(2) = 20; // at修改
// 此时v1:[10, 3, 20]
// 5. 删除元素
v1.pop_back(); // 尾部删除(效率高)
v1.erase(v1.begin() + 1); // 中间删除(删除索引1的元素)
// 此时v1:[10]
// 6. 常用操作
cout << "\nvector大小(元素个数):" << v1.size() << endl; // 输出:1
cout << "vector容量(可存储元素个数):" << v1.capacity() << endl; // 容量 >= 大小
v1.resize(3, 0); // 调整大小为3,新增元素初始化为0(此时v1:[10, 0, 0])
if (v1.empty()) {
cout << "vector为空" << endl;
} else {
cout << "vector非空" << endl;
}
// 7. 排序(需包含<algorithm>头文件)
vector<int> v5 = {3, 1, 4, 1, 5};
sort(v5.begin(), v5.end()); // 升序排序
cout << "\n排序后v5:";
for (auto x : v5) {
cout << x << " ";
}
cout << endl; // 输出:1 1 3 4 5
// 8. 清空与释放内存
v5.clear(); // 清空元素(大小变为0,容量不变)
vector<int>().swap(v5); // 释放多余容量(容量变为0)
return 0;
}三、vector的扩容机制
vector的底层是连续内存,当插入元素导致容量不足时,会自动扩容,扩容规则通常是:每次扩容为当前容量的2倍(不同编译器可能略有差异)。
扩容过程:
- 分配一块新的、容量为当前2倍的内存;
- 将原有内存中的所有元素拷贝到新内存;
- 释放原有内存;
- 将vector的指针指向新内存。
⚠️ 注意:频繁扩容会导致性能损耗(拷贝元素),若已知元素数量,可提前预留容量:
cpp
vector<int> v;
v.reserve(100); // 提前预留100个元素的容量,避免频繁扩容四、vector与set、map的对比(补充,帮助理解定位)
| 容器类型 | 容器类别 | 核心特点 | 访问效率 | 插入删除效率(中间) | 适用场景 |
|---|---|---|---|---|---|
| vector | 序列式 | 连续内存、随机访问、无去重 | O(1)(下标) | O(n) | 频繁访问、尾部插入删除、需要排序 |
| set | 关联式 | 红黑树、值为key、去重、有序 | O(log n) | O(log n) | 去重、有序查找、不需要key-value映射 |
| map | 关联式 | 红黑树、key-value映射、key唯一 | O(log n) | O(log n) | 关键字映射、需要通过key查找value |
五、常见误区与注意事项
- 下标访问越界:vector的[]操作符无越界检查,访问超出size的下标会导致未定义行为,建议用at()访问(有越界检查);
- 混淆size和capacity:size是当前元素个数,capacity是当前可存储的最大元素个数,size <= capacity;
- 中间插入删除效率低:vector中间插入/删除会移动后续所有元素,数据量较大时,建议用list(双向链表);
- 清空不释放内存:clear()仅清空元素(size=0),capacity不变,需用swap()释放多余容量;
- 迭代器失效:vector扩容后,原有迭代器会失效(指向原有内存),扩容后需重新获取迭代器。
六、适用场景总结
- 频繁通过下标访问元素(如:数组遍历、随机访问);
- 主要在尾部进行插入/删除操作(如:收集数据、拼接数组);
- 需要对元素进行排序、查找等算法操作(与algorithm配合使用);
- 数据量不大,或可提前预估数据量(避免频繁扩容)。
vector是STL中最基础、最常用的容器,与set、map、stack等容器配合使用,可覆盖绝大多数开发场景。
