目录
一:介绍
- vector是STL容器中的一种常用的容器,和数组类似,由于其大小(size)可变,常用于数组大小不可知的情况下来替代数组。
- vector也是一种顺序容器,在内存中连续排列,因此可以通过下标快速访问,时间复杂度为O(1)。然而,连续排列也意味着大小固定,数据超过vector的预定值时vector将自动扩容。
二:vector的创建和方法
首先,使用vector时需包含头文件:
- #include <vector>
创建vector
vector本质是类模板,可以存储任何类型的数据。数组在声明前需要加上数据类型,而vector则通过模板参量设定类型。
比如,声明一个int型的vector数组。
vector<int> arr1; //一个空数组
// 这里使用了c++11的新特性 initializer_list<int> 常量数组的写法
vector<int> arr2 = {
1, 2, 3, 4, 5}; //包含1、2、3、4、5五个变量
vector<int> arr3(4); //开辟4个空间,值默认为0
vector<int> arr4(5, 3); //5个值为3的数组
//拷贝构造
vector<int> arr5(arr4); //将arr4的所有值复制进去,和arr4一样
//迭代器版本的拷贝构造
vector<int> arr6(arr4.begin(), arr4.end()); //将arr4的值从头开始到尾复制
vector<int> arr7(arr4.rbegin(), arr4.rend()); //将arr4的值从尾到头复制
方法
✨iterators(迭代器) \colorbox{pink}{✨iterators(迭代器)} ✨iterators(迭代器)
名字 | 描述 |
---|---|
begin | 返回指向容器中第一个元素的迭代器。 |
end | 返回指向容器最后一个元素所在位置后一个位置的迭代器 |
rbegin | 返回容器逆序的第一个元素的迭代器 |
rend | 返回容器逆序的最后一个元素的前一个位置的迭代器 |
cbegin | 和begin()功能相同,在其基础上增加了 const 属性,不能用于修改元素。 |
cend | 和end()功能相同,在其基础上增加了 const 属性,不能用于修改元素。 |
crbegin | 和rbegin()功能相同,在其基础上增加了 const 属性,不能用于修改元素。 |
crend | 和rend()功能相同,在其基础上增加了 const 属性,不能用于修改元素。 |
✨Capacity(容量) \colorbox{pink}{✨Capacity(容量)} ✨Capacity(容量)
名字 | 描述 |
---|---|
size | 返回实际元素的个数 |
capacity | 返回总共可以容纳的元素个数 |
max_size | 返回元素个数的最大值。这个值非常大,一般是2^32-1 |
empty | 判断vector是否为空,为空返回true否则false |
resize | 改变实际元素的个数,对应于size |
reserve | 增加容器的容量,控制vector的预留空间, 不会初始化不能直接访问后面的元素 |
shrink_to_fit | 减少capacity到size的大小 |
✨Element access(元素访问) \colorbox{pink}{✨Element access(元素访问)} ✨Element access(元素访问)
名字 | 描述 |
---|---|
operator[] | vector可以和数组一样用[]访问元素 |
at | vector.at(i)等同于vector[i],访问数组下表的元素 |
front | 返回第一个元素 |
back | 返回最后一个元素 |
data | 返回指向容器中第一个元素的指针 |
✨Modifiers(修改器) \colorbox{pink}{✨Modifiers(修改器)} ✨Modifiers(修改器)
名字 | 描述 |
---|---|
push_back | 在容器的尾部插入元素 |
pop_back | 删除最后一个元素 |
insert | 插入元素 |
erase | 删除元素 |
clear | 清除容器内容,size=0,存储空间不变 |
swap | 交换两个元素的所有内容 |
assign | 用新元素替换原有内容。 |
emplace | 插入元素,和insert实现原理不同,速度更快 |
emplace_back | 在容器的尾部插入元素,和push_back不同, 速度更快 |
三:vector的具体用法
3.1 push_back、pop_back 和 emplace_back
vector<int> arr;
for (int i = 0; i < 5; i++)
{
arr.push_back(i);
}
for (int i = 0; i < 5; i++)
{
arr.pop_back();
}
- emplace_back的效果和push_back一样,都是尾部插入元素
arr.emplace(10);
两者的差别在于底层实现的机制不同:push_back将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。所以emplace_back的速度更快。
3.2 insert、emplace
注意:这里存在迭代器失效的问题
如果在某一位置例如:iterator pos = arr.begin() + 3 后续就尽量不要再使用pos这个迭代器
原因:insert可能导致扩容 原空间可能发生了变化,pos就相当于野指针了扫描二维码关注公众号,回复: 17516506 查看本文章![]()
insert有三种用法:
- 在指定位置插入值为val的元素。(传入的位置都是迭代器)
//在arr的头部插入值为10的元素
vector<int> arr;
arr.insert(arr.begin(), 10);
- 在指定位置插入n个值为val的元素
//从arr的头部开始,连续插入3个值为10的元素
vector<int> arr;
arr.insert(arr.begin(), 3, 10);
- 在指定位置插入区间[start, end]的所有元素
//从arr的头部开始,连续插入arrs区间[begin, end]的所有元素
vector<int> arr;
vector<int> arrs = {
1, 2, 3, 4, 5 };
arr.insert(arr.begin(), arrs.begin(), arrs.end());
emplace和insert同为插入元素,不过emplace只能插入一个元素:
//在arr的头部插入值为10的元素
vector<int> arr;
arr.emplace(arr.begin(), 10);
//insert和emplace的区别和上面类似,就是一个是拷贝和复制的过程,而另一个则是直接创建一个新元素。
3.3 erase
erase通过迭代器删除某个或某个范围的元素,并返回下一个元素的迭代器。
注意:这里存在迭代器失效的问题,和 insert 一样, 原空间发生了变化
---- vector的删除操作只会导致指向被删除元素及后面的迭代器失效 而list只是当前位置的迭代器失效
vector<int> arr{
1, 2, 3, 4, 5};
// 删除arr开头往后偏移两个位置的元素,即arr的第三个元素,3
arr.erase(arr.begin() + 2);
// 删除arr.begin()到arr.begin()+2之间的元素,删除两个;即删除arr.begin()而不到arr.begin()+2的元素
arr.erase(arr.begin(), arr.begin() + 2);
3.4 assign
assign修改vector,和insert操作类似,不过insert是从尾部插入,而assign则将整个vector改变
---------------------------------------------------------------------------注意会影响size的大小---------------------------------------------------------------------------
- 将整个vector修改为n个值为val的容器
//将arr修改为3个值为5的vector。
vector<int> arr = {
5, 4, 3, 2, 1};
arr.assign(3, 5);
- 将整个vector修改为某个容器[start, end]范围内的元素
//将arr修改为范围[arrs.begin, arrs.end]内的元素
vector<int> arr1 = {
5, 4, 3, 2, 1};
vector<int> arr2 = {
1, 2, 3, 4, 5 };
arr1.assign(arr2.begin(), arr2.end());
- 用数组的值进行范围修改
//将arr替换为数组arrs
vector<int> arr = {
5, 4, 3, 2, 1};
int arrs[5] = {
1, 2, 3, 4, 5 };
arr.assign(arrs, arrs + 5);
四:vector 元素的操作(sort、reverse等)
需要用到头文件 中的算法
1、排序 sort()
使用到的函数为 sort() :按输入序列的字典序升序排序,原位操作,无返回值
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(void)
{
vector<int> a{
2, 0, 2, 2, 0, 3, 0, 9};
sort(a.begin(), a.end()); //原位操作
// sort(a.begin(), a.end(), greater<int>) 升序操作
for(auto i : a)
cout << i << " ";
return 0;
}
// 输出结果 //
0 0 0 2 2 2 3 9
2、消除相邻的重复元素 unique()
使用到的函数为 unique() :将输入序列相邻的重复项“消除”,返回一个指向不重复值范围末尾的迭代器,一般配合 sort() 使用
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(void)
{
vector<int> a{
2, 0, 2, 2, 0, 3, 0, 9};
sort(a.begin(), a.end()); // 先排序
for(int i:a) cout << i << " "; // 输出
cout << endl;
auto end_unique = unique(a.begin(), a.end()); //将输入序列相邻的重复项“消除”,返回一个指向不重复值范围末尾的迭代器
a.erase(end_unique, a.end()); // 删除末尾元素
for(int i:a) cout << i << " "; // 输出
return 0;
}
// 运行结果 //
0 0 0 2 2 2 3 9
0 2 3 9
3、逆序 reverse()
方法1:使用到的函数为 reverse() :将输入序列按照下标逆序排列,原位操作,无返回值
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main(void)
{
vector<int> a{
2, 0, 2, 2, 0, 3, 0, 9};
reverse(a.begin(), a.end()); // 原位逆序排列
for(int i:a) cout << i << " "; // 输出
return 0;
}
// 运行结果 //
9 0 3 0 2 2 0 2
五:vector 中找最值
同样需要用到头文件 中的算法。
1、最大值auto it = max_element(v.begin, v,end()),返回最大值的迭代器
2、最小值 auto it = min_element(v.begin, v,end()),返回最小值的迭代器
3、相对位置大小 auto b = distance(x, y),x、y 是迭代器类型,返回 x、y 之间的距离,可以用来获取最大/小值的索引
六:vector扩容
注意:每次扩容新空间不能太大,也不能太小,太大容易造成空间浪费,太小则会导致频繁扩容而影响程序效率。
1.如何避免扩容导致效率低
- 1.如果要避免扩容而导致程序效率过低问题,其实非常简单:如果在插入之前,可以预估vector存储元素的个数,提前将底层容量开辟好即可。如果插入之前进行reserve,只要空间给足,则插入时不会扩容,如果没有reserve,则会边插入边扩容,效率极其低下。
- 2.以倍数的方式扩容比以等长个数的扩容方式效率高。
- vector在push_back以成倍增长可以在均摊后达到O(1)的事件复杂度,相对于增长指定大小的O(n)时间复杂度更好。
- 为了防止申请内存的浪费,现在使用较多的有2倍与1.5倍的增长方式,而1.5倍的增长方式可以更好的实现对内存的重复利用
总结
vector在项目或者刷题中有大量的运用,熟练掌握他们的用法是必不可少的一步, 在面试时vector的扩容问题也会经常被问到, 希望我们共同进步, 有错误还请在评论区指正!