参考这一篇很详尽C++ vector 容器浅析
vector 翻译为向量, 但是这里使用 “变长数组”的叫法更容易理解,也即 “长度根据需要而自动改变的数组”。 在考试题中,有时会碰到只用普通数组会超过内存的情况, 这种情况使用vector可以解决很多问题。 另外,vector还可以用来以邻接表的方式存储图,这对无法使用邻接矩阵的题目(节点数太多)、又害怕使用指针实现邻接表的朋友是非常友好的。
如果要使用vector ,则需要添加vector的头文件,即 #include<vector> , 此外还需要在头文件下加上 using namespace std;
1、vector的定义
单独定义一个vector:
vector<typename> name;
上面这个定义其实就相当于是一维数组name[SIZE],只不过其长度可以根据需要进行变化,比较节省空间,通俗说了就是“变长数组”。
和一维数组一样,这里的 typename 可以是任何基本类型, 例如int、double、char、结构体等,也可以是STL标准容器,例如vector、set、queue等。
vector<int> name;
vector<double> name;
vector<char> name;
vector<node> name; //node是结构体类型
vector<int> v1; //定义元素为int的向量v1
vector<int> v2(10); //指定向量v2的初始大小为10个int元素
vector<double> v3(10,1.23); //指定v3的10个初始元素的初值为1.23
vector<int> v4(a,a+5); //用数组a[0...4]共5个元素初始化v4
需要注意一点,如果 typename 也是一个STL容器,定义的时候要记得在> >之间加一个空格,否则编译器会把它视作移位操作,导致编译错误。
vector <vector<int> > name;
可以很容易联想到二维数组的定义,即其中一维是一个数组的数组。那么二维 vector 数组,即Arrayname[]中的每一个元素都是vector。可以把二维vector数组理解为两个维都可变长的二维数组。
定义vector数组的方法:
vector<typename> Arrayname[arraySize];
例如:
vector<int> vi[10];
这样Arrayname[0]~Arrayname[arraySize-1]中的每一个元素都是一个vector容器。
与vector <vector<int> > name 不同的是,这种写法的一维长度已经固定为arraySize,另一维才是“变长”的。
2、vector容器内元素的访问
一般有两种方式:通过下标访问和通过迭代器访问。
(1)通过下标访问
和普通的数组一样,对于一个定义为 vector<typename> vi 的vector容器来说,直接访问 vi[index] 即可(如vi[0], vi[1])。
(2)通过迭代器访问
迭代器(iterator)可以理解为一种类似指针的东西,其定义是:
vector<typename> :: iterator it;
这样 it 就是一个 vector<typename> :: iterator 型的变量,其中typename就是定义vector是填写的类型。
vector<int> :: iterator it;
vector<double> :: iterator it;
这样就得到了迭代器it, 并且可以通过 *it 来访问 vector 里面的元素。
例如有这样一个vector容器:
vector<int> vi;
for(int i = 1; i <= 5; i++ ) {
vi.push_back(i); //push_back(i) 在 vi 的末尾添加元素i ,即依次添加 1,2,3,4,5
}
可以通过类似下标和指针访问数组的方式来访问容器内的元素:
#include<cstdio>
#include<vector>
using namespace std;
int main(){
vector<int> vi;
for(int i=1; i<=5; i++){
vi.push_back(i);
}
//vi.begin()为取vi的首元素地址,而it指向这个地址
vector<int>::iterator it = vi.begin();
for(int i=0;i<5;i++){
printf("%5d", *(it+i)); //输出vi[i]
}
return 0;
}
输出结果:
从这里可以看出, vi[i] 和 (vi.begin() + i)是等价的。
既然上面说到了 begin()函数的作用是为取vi的首元素地址,那么还有end()函数,end() 并不是取 vi 的尾元素地址,而是取尾元素地址的下一个地址。end()作为迭代器的末尾标志,不存储任何元素,这里的begin()和end() “左闭右开”。
除此之外,迭代器还实现了两种自加操作: ++it 和 it++(自减操作同理),于是有了另一种遍历vector的写法:
#include<cstdio>
#include<vector>
using namespace std;
int main(){
vector<int> vi;
for(int i=1;i<=5;i++){
vi.push_back(i);
}
//vector的迭代器不支持 it<vi.end() 写法,因此循环条件只能用 it!=vi.end()
for(vector<int>::iterator it=vi.begin(); it!=vi.end(); it++){
printf("%5d",*it);
}
return 0;
}
最后需要指出,在常用的STL容器中,只有vector和string中,才允许使用vi.begin()+3这种迭代器加上整数的写法。
3.vector常用函数
(1)push_back( )
push_back( )就是在vector后面添加一个元素x,时间复杂度为O(1)。
示例如下:
#include<cstdio>
#include<vector>
using namespace std;
int main(){
vector<int> vi;
for(int i=1;i<=5;i++){
vi.push_back(i);
}
for(int i=0;i<vi.size();i++){ //vi.size()返回vi中元素的个数
printf("%5d",vi[i]); //这里直接通过下标访问
}
}
(2)pop_back()
pop_back()用于删除vector的尾元素,时间复杂度为O(1)。
示例如下:
#include<cstdio>
#include<vector>
using namespace std;
int main(){
vector<int> vi;
for(int i=1;i<=5;i++){
vi.push_back(i);
}
vi.pop_back(); //删除vi的尾元素5
for(int i=0;i<vi.size();i++){ //vi.size()返回vi中元素的个数
printf("%5d",vi[i]); //这里直接通过下标访问
}
}
(3)size()
size()用来获取vector中元素的个数,时间复杂度为O(1)。size()返回的是unsigned类型,不过一般来说%d也不会有什么问题。
(4)clear()
clear()用来清空vector中的所有元素,时间复杂度为O(N)。
#include<cstdio>
#include<vector>
using namespace std;
int main(){
vector<int> vi;
for(int i=1;i<=5;i++){
vi.push_back(i);
}
vi.clear(); //清空vi中的所有元素
printf("%d",vi.size());
}
(5)insert()
insert(it,x)用来向vector的任意迭代器it处插入一个元素x,时间复杂度为O(N)。
#include<cstdio>
#include<vector>
using namespace std;
int main(){
vector<int> vi;
for(int i=1;i<=5;i++){
vi.push_back(i);
}
vi.insert(vi.begin()+2,-99); //将-99插入到vi[2]的位置
for(int i=0;i<vi.size();i++){
printf("%5d",vi[i]);
}
}
输出结果:
(6)erase( )
erase( )有两种用法:删除单个元素、删除一个区间内所有元素,时间复杂度均为O(N)。
①删除单个元素
erase(it),即删除迭代器为it处的元素。
示例:
#include<cstdio>
#include<vector>
using namespace std;
int main(){
vector<int> vi;
for(int i=5;i<=9;i++){
vi.push_back(i); //插入5,6,7,8,9
}
vi.erase(vi.begin()+3); //删除元素 8
for(int i=0;i<vi.size();i++){ //vi.size()返回vi中元素的个数
printf("%5d",vi[i]); //这里直接通过下标访问
}
}
②删除一个区间内的所有元素
erase(first,last); 其中first为需要删除的区间的起始迭代器,而last则为需要删除的区间的末尾迭代器的下一个地址,也即为删除左闭右开的区间 [first,last) 。
示例:
#include<cstdio>
#include<vector>
using namespace std;
int main(){
vector<int> vi;
for(int i=5;i<=9;i++){
vi.push_back(i); //插入5,6,7,8,9
}
vi.erase(vi.begin()+1, vi.begin()+4); //删除元素 6,7,8 //注意右边是开的
for(int i=0;i<vi.size();i++){ //vi.size()返回vi中元素的个数
printf("%5d",vi[i]); //这里直接通过下标访问
}
}
如果要删除这vector里面的所有元素,正确的写法是 vi.erase(vi.begin(), vi.end()),当然最方便的清空是 vi.clear();
4、vector的常见用途
(1)存储数据
①vector本身可以作为数组使用,而且在一些元素个数不确定的场合,可以很好的节省空间。
②有些场合需要根据一些条件把部分数据输出在同一行,数据之间中空格隔开,由于输出数据的个数是不确定的,为了更方便的处理最后一个满足条件的数据后面不输出空格,可以先用vector记录所有需要输出的数据,然后一次性输出。
(2)用邻接表存储图
使用vector实现邻接表,可以替代指针的写法。
这里要补充一下vector的构造函数:
- vector():创建一个空vector
- vector(int nSize):创建一个vector,元素个数为nSize
- vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
- vector(const vector&):复制构造函数
- vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中