1.vector的介绍及使用
1.1 vector的介绍
std::vector是 C++ 标准库中的动态数组 ,可以自动管理内存 ,在运行时动态调整大小
1.2 vector的使用
1.2.1 vector的定义
|------------------------------------------------------------|--------------|
| 构造函数声明 | 接口说明 |
| vector()(重点) | 无参构造 |
| vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
| vector (const vector& x); (重点) | 拷贝构造 |
| vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |
演示代码:
cpp
#include<iostream>
#include<vector>
using namespace std;
//自定义的输出函数,vector无法直接cout输出,这便是最基础的方法
void my_print(vector<int> nums)
{
for (size_t i = 0; i < nums.size(); i++)
{
cout << nums[i]<<' ';
}
cout << endl;
}
int main()
{
vector<int> v1;
vector<int> v2(5, 2);//创建包含5个2的vector
//若只有一个参数,则创建该参数个数的0
vector<int> v3(v2);//拷贝构造,拷贝v2
vector<int> v4 = v2;//同2,也是拷贝构造
vector<int> v5 = { 1, 2, 3, 4, 5, 6, 7};//直接给vector赋值
vector<int> v6(v5.begin() + 1, v5.end()-2);//{2,3,4,5}
//输出
my_print(v1);
my_print(v2);
my_print(v3);
my_print(v4);
my_print(v5);
my_print(v6);
}
1.2.2 vector iterator 的使用
|-----------------|---------------------------------------------------------------------------|
| iterator的使用 | 接口说明 |
| begin + end(重点) | 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下 一个位置的iterator/const_iterato |
| rbegin + rend | 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置 的reverse_iterator |


迭代器还没太学会的同学建议学一下
1.2.3 vector 空间增长问题
|--------------|-------------------|
| 容量空间 | 接口说明 |
| size | 获取数据个数 |
| capacity | 获取容量大小 |
| empty | 判断是否为空 |
| resize(重点) | 改变vector的size |
| reserve (重点) | 改变vector的capacity |
- capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2 倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是 根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。 reserve只负责开辟空间,如果确定知道需要用多少空间,
- reserve可以缓解vector增容的代 价缺陷问题。
- resize在开空间的同时还会进行初始化,影响size。
测试代码:
cpp
#include<iostream>
#include<vector>
using namespace std;
//自定义的输出函数,vector无法直接cout输出,这便是最基础的方法
void my_print(vector<int> nums)
{
for (size_t i = 0; i < nums.size(); i++)
{
cout << nums[i]<<' ';
}
cout << endl;
}
int main()
{
vector<int> v1;
cout <<"此时v1是否为空:" << v1.empty() << endl;//判断v1是否为空
cout << "尾插数字1" << endl;
v1.push_back(1);
cout << "尾插数字2" << endl;
v1.push_back(2);
cout << "尾插数字3" << endl;
v1.push_back(3);
cout << "此时v1是否为空:" << v1.empty() << endl;//判断v1是否为空
cout << "此时v1的v1.size():" << v1.size()<<endl;//v1的有效数据个数
cout << "此时v1的v1.capacity():" << v1.capacity() << endl;//v1的容量大小
cout << "插入后的v1:";
my_print(v1);
v1.resize(5);//将v1的有效数据个数改为5,不足5的部分默认用0填充
cout << "resize(5)的v1:";
my_print(v1);
v1.resize(2);//将v1的有效数据个数改为2,超出2的部分直接删
cout << "resize(2)的v1:";
my_print(v1);
//reserve改变vector的capacity,不改变有效数据
v1.reserve(20);
cout<<"reserve后v1的capacity:" << v1.capacity();
}
1.2.4 vector 增删查改
|-------------------|--------------------------------|
| vector 增删查改 | 接口说明 |
| push_back(重点) | 尾插 |
| pop_back (重点) | 尾删 |
| find | 查找。(注意这个是算法模块实现,不是vector的成员接口) |
| insert | 在position之前插入val |
| erase | 删除position位置的数据 |
| swap | 交换两个vector的数据空间 |
| operator[] (重点) | 像数组一样访问 |
push_back(const T& val)(重点)
-
功能 :在 vector 的末尾添加一个元素(尾插)
-
示例:
cpp
std::vector<int> v;
v.push_back(10); // v: [10]
v.push_back(20); // v: [10, 20]
v.push_back(30); // v: [10, 20, 30]
// 也可以插入自定义类型
struct Point {
int x, y;
};
std::vector<Point> points;
points.push_back({1, 2});
points.push_back(Point{3, 4});
pop_back()(重点)
-
功能 :删除 vector 的最后一个元素(尾删)
-
注意:
-
不返回被删除的元素
-
如果 vector 为空,行为未定义(可能导致崩溃)
-
-
时间复杂度:O(1)
-
示例
cpp
std::vector<int> v = {1, 2, 3, 4, 5};
v.pop_back(); // v: [1, 2, 3, 4]
v.pop_back(); // v: [1, 2, 3]
// 常见用法:删除末尾元素
while (!v.empty())
{
v.pop_back(); // 逐个删除所有元素
}
find(算法,非成员函数)
-
功能:在指定范围内查找特定值
-
所在头文件 :
<algorithm> -
返回值 :指向找到元素的迭代器 ,如果没找到则返回
end() -
时间复杂度:O(n)
-
示例:
cpp
#include <iostream>
#include <vector>
#include <algorithm> // 需要包含这个头文件
int main()
{
std::vector<int> v = {1, 2, 3, 4, 5};
// 查找值为3的元素
auto it = std::find(v.begin(), v.end(), 3);
if (it != v.end())
{
std::cout << "找到3,位置在索引: "
<< std::distance(v.begin(), it) << std::endl;
std::cout << "值是: " << *it << std::endl;
}
else
{
std::cout << "没找到" << std::endl;
}
// 查找不存在的值
auto it2 = std::find(v.begin(), v.end(), 99);
//查询到最后位置仍未找到
if (it2 == v.end())
{
std::cout << "99不存在" << std::endl;
}
return 0;
}
insert(iterator position, const T& val)
-
功能 :在指定位置之前插入一个元素
-
参数:
-
position:插入位置的迭代器 -
val:要插入的值
-
-
返回值:指向新插入元素的迭代器
-
时间复杂度:O(n)(可能需要移动元素)
-
示例:
cpp
std::vector<int> v = {1, 2, 4, 5};
// 在索引2的位置(值为4)之前插入3
auto it = v.insert(v.begin() + 2, 3);
// v: [1, 2, 3, 4, 5]
// it指向新插入的3
// 在开头插入0
v.insert(v.begin(), 0);
// v: [0, 1, 2, 3, 4, 5]
// 在末尾插入6
v.insert(v.end(), 6);
// v: [0, 1, 2, 3, 4, 5, 6]
// 插入多个相同元素
v.insert(v.begin() + 2, 3, 100);
// 在索引2处插入3个100
// 插入迭代器范围
std::vector<int> source = {7, 8, 9};
//第一个参数是插入的位置,后两个参数是插入内容的开始和结尾
v.insert(v.end(), source.begin(), source.end());
// 将source的所有元素插入到v末尾
erase(iterator position)
-
功能:删除指定位置的元素
-
参数:要删除元素的迭代器
-
返回值:指向被删除元素之后位置的迭代器
-
时间复杂度:O(n)(可能需要移动元素)
-
注意:删除操作会使迭代器失效,需要重新获取(后文会详细讲)
-
示例:
cpp
std::vector<int> v = {1, 2, 3, 4, 5};
// 删除索引2的元素(值为3)
auto it = v.erase(v.begin() + 2);
// v: [1, 2, 4, 5]
// it指向原来的索引3(现在的索引2,值为4)
// 删除开头的元素
v.erase(v.begin());
// v: [2, 4, 5]
// 删除末尾的元素
v.erase(v.end() - 1);
// v: [2, 4]
范围删除:
cpp
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7};
// 删除索引2到索引5(不包括5)的元素
auto it = v.erase(v.begin() + 2, v.begin() + 5);
// v: [1, 2, 6, 7]
// it指向原来的索引5(现在的索引2,值为6)
swap(vector& other)
-
功能:交换两个 vector 的所有内容
-
特点:快速,只交换内部指针,不复制元素
-
时间复杂度:O(1)
-
示例
cpp
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = {4, 5, 6, 7};
std::swap(v1, v2);
// v1: [4, 5, 6, 7]
// v2: [1, 2, 3]
// capacity也会交换
operator[](重点)
-
功能:通过下标访问元素(像数组一样)
-
特点:
-
不检查边界
-
速度快
-
-
时间复杂度:O(1)
-
示例:
cpp
std::vector<int> v = {1, 2, 3, 4, 5};
// 读取元素
std::cout << v[0] << std::endl; // 1
std::cout << v[2] << std::endl; // 3
// 修改元素
v[1] = 20; // v: [1, 20, 3, 4, 5]
v[3] = v[4] * 2; // v[3] = 5 * 2 = 10
// 遍历
for (size_t i = 0; i < v.size(); ++i) {
std::cout << v[i] << " ";
}
1.2.5 vector 迭代器失效问题。(重点)
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对 指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即:如果继续使用已经失效的迭代器,程序可能会崩溃)。
举个例子:你记得朋友住在1号房间,但酒店重新装修后,1号房间可能变成仓库了,你还按老地址去找就会出错!
对于vector可能会导致其迭代器失效的操作有:
1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、 assign、push_back等。
cpp
#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{1,2,3,4,5,6};
auto it = v.begin();
// 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
// v.resize(100, 8);
// reserve的作用就是改变扩容大小但不改变有效元素个数
//操作期间可能会引起底层容量改变
// v.reserve(100);
// 插入元素期间,可能会引起扩容,而导致原空间被释放
// v.insert(v.begin(), 0);
// v.push_back(8);
// 给vector重新赋值,可能会引起底层容量改变
//v.assign(100, 8);
/*
出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释
放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块
已经被释放的空间,而引起代码运行时崩溃。
解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给
it重新赋值即可。
*/
while(it != v.end())
{
cout<< *it << " " ;
++it;
}
cout<<endl;
return 0;
}
- 指定位置元素的删除操作--erase
cpp
#include<iostream>
using namespace std;
#include <vector>
int main()
{
int a[] = { 1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
// 删除pos位置的数据,导致pos迭代器失效。
v.erase(pos);
cout << *pos << endl; // 此处会导致非法访问
return 0;
}
erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理 论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end 的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素 时,vs就认为该位置迭代器失效了。
这编译器事儿这么多?
严谨点儿总是没错的,养成好的编码习惯嘛。那问题来了!我们怎么防止迭代器失效呢?
就是及时更新迭代器,老的失效换成新的不就行了吗?
cpp
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
//方法1:接收返回值
std::cout << "方法1:接收返回值" << std::endl;
auto it = v.begin() + 1; // 指向2
std::cout << "原it指向: " << *it << std::endl;
// 删除2,同时自动更新it指向3
it = v.erase(it);
std::cout << "删除后it自动指向: " << *it << std::endl; // 3
//方法2:重新获取
std::cout << "\n方法2:重新获取" << std::endl;
v = {1, 2, 3, 4, 5};
size_t index = 1; // 记住索引1(值2)
// 删除索引1的元素(值2)
v.erase(v.begin() + index);
// 重新获取迭代器(但要注意元素已移动)
it = v.begin() + index; // 现在指向3
std::cout << "重新获取后it指向: " << *it << std::endl; // 3
//特殊情况:push_back
std::cout << "\n特殊情况:push_back" << std::endl;
it = v.begin(); // 指向1
// push_back不返回迭代器
v.push_back(6); // 可能重新分配内存
// it可能失效,必须重新获取
it = v.begin(); // 重新获取
std::cout << "push_back后重新获取it指向: " << *it << std::endl; // 1
return 0;
}
学会了就给博主点个赞呗?(✪ω✪)
---------(如有问题,欢迎评论区提问)---------
