STL<vector>用法汇总
2016-09-25 13:16
393 查看
使用场合:
vector算是一个比较万金油的容器,它是一个可变大小数组,支持随机访问,不过在尾部以外的位置进行增加和删除操作会比较耗时。通常用vector来代替原始的数组来使用,比较方便。
声明与初始化:
首先要包含头文件,vector的头文件名就是< vector >。
声明方式:
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve;//声明一个存储int型数据的容器ve vector<vector<int>> vve;//声明一个二维的vector,C++11标准写法 vector<vector<string> > vvs;//两个尖角括号之间要留一个空格,旧编译器的写法 return 0; }1
2
3
4
5
6
7
8
9
10
11
初始化:
vector的初始化方式很多,书上介绍的如下#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> va;//调用默认构造函数,里面什么也没有 for(int i=0;i<5;i++) va.push_back(i);//放5个元素进去 vector<int> vb(va);//用va初始化vb,此种用法要求va与vb必须是同一种容器,且类型相同 vector<int> vc{1,2,3,4};//初始化列表 vector<int> vc2={1,2,3,4};//同上,C++11新标准 vector<int> vd(va.begin(),va.end());//用迭代器指定的范围初始化 list<int> li={2,3,4}; vector<int> vli(li.begin(),li.end())//使用迭代器可以把不同容器,类型相同的元素用来初始化 vector<int> ve(10);//包含10初始化值的元素,在ve当中里面有10个0次构造函数是explicit vector<int> vf(10,1);//在vf里面塞进10个1 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
赋值操作:
利用拷贝构造函数和swap函数可以实现赋值操作#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> va,vb,vc; va={1,2,3,4};//用C++11的初始化列表来赋值 for(int i=1;i<=10;i++)//在vb中塞入10个数 vb.push_back(i); va=vb;//把vb拷贝给va swap(va,vc);//交换va和vc for(auto x:vc)//输出应该是va里面的值 cout<<x<<" "; for(auto x:va)//va里面应该什么也没有 cout<<x<<" "; cout<<endl; va.swap(vc);//再把vc和va换回来,用成员函数的形式 for(auto x:vc)//里面什么也没有 cout<<x<<" "; for(auto x:va)// cout<<x<<" "; return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
还可以利用assign函数实现
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> vb,va={1,3,5,7,9}; vb.assign(va.begin(),va.begin()+3);//使用va的迭代器给vb赋值 //这里注意,不可以自己给自己用迭代器赋值 for(int i=0;i<vb.size();i++) cout<<vb[i]<<endl;//输出1 3 5 vb.assign({2,4,6});//里面可以放一个初始化列表 for(int i=0;i<vb.size();i++) cout<<vb[i]<<endl;//输出2 4 6 list<int> li={2,4,6,8}; vb.assign(li.begin(),li.end());//可以把其他容器,但是类型相同的迭代器用来赋值 for(int i=0;i<vb.size();i++) cout<<vb[i]<<endl; list<string> names={"abc","efg"}; vector<const char*> oldstyle={"qwe","ert"}; names.assign(oldstyle.begin(),oldstyle.end());//vector给list初始化,也可以用cbegin //把const cahr*赋值给string for(auto x:names) cout<<x<<endl;//qwe ert vector<string> vc; vc.assign(3,"abc");//向vc中塞入3个abc for(int i=0;i<vc.size();i++) cout<<vc[i]<<endl;//abc abc abc return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
vector的比较
vector重载了运算符<,>,==,<=,>=等比较符号。其比较规则与字典序类似,如果两个容器具有相同大小,而且每个元素对应相等,那么这两个vector相同。 如果两个vector大小不同,但是公有的元素和对应位置全都相同,那么大的容器比小的容器大。
如果两个容器完全不同,那么,比较第一个不同的元素哪个大那个小作为判断标准。
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> v1={1,3,5}; vector<int> v2={1,4}; vector<int> v3={1,3,5}; cout<<(v1<v2)<<endl;//true cout<<(v1==v3)<<endl;//true return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
插入元素:
vector当中插入元素主要使用push_back向后面插入一个元素。 在vector当中没有push_front的用法!
如果想再任意位置插入一个元素,可以用insert成员函数,同样会涉及到数据移动。
具体用法:
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve; //ve.push_back(e)在ve的尾部添加一个e元素 ve.push_back(1);//在尾部插入一个1 //ve.insert(it,n,e)//在it指向元素的前面添加n个元素e ve.insert(ve.end(),1,2);//相当于在尾部添加1个2 ve.insert(ve.begin(),1,0);//在头部添加1个0 //ve.insert(it,beg,ed)//在it指向元素的前面添加迭代器[beg,ed)范围内的元素 vector<int> v={-2,-1}; ve.insert(ve.begin(),v.begin(),v.end());//在ve的前面添加v的元素 //ve.insert(it,li)在it所指的元素的前面添加一个列表li ve.insert(ve.end(),{3,4,5}); //错误用法 //ve.insert(ve.begin(),ve.begin(),ve.end())不能用调用对象自己的迭代器当做赋值范围 for(auto x:ve) cout<<x<<endl; return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
这里的push_back操作是对拷贝值进行操作。
在C++11标准当中,insert函数具有返回值,返回第一个新加入元素的迭代器(不能是插入列表)。
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve={1,2,3,4}; auto it=ve.insert(ve.end(),5);//返回指向最后一个元素的迭代器 cout<<*it<<endl;//5 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
emplace操作:
当调用emplace操作的函数时,每次会新构造一个对象,并添加到容器当中。添加的方法要和类当中的构造函数相匹配。 在vector当中有c.emplace()和c.emplace_back()操作两种。参数当中可以
#include <bits/stdc++.h> using namespace std; class A { public: int a; string s; A(){a=0,s="";} A(int aa,string ss){a=aa,s=ss;} }; int main() { ios::sync_with_stdio(false); vector<A> va; va.emplace_back(1,"abc");//添加 va.emplace_back();//添加一个默认构造函数 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
也可以调用c.emplace(it,arg);其中it为指向一个数据的迭代器,arg为参数。
访问元素:
清空vector可以使用成员函数c.clear()判断vector是否为空,可以使用成员函数empty(),如果为空返回true,否则返回false
vector输出最后一个元素的引用可以用back()成员函数,如果容器为空,则行为未定义
vector输出第一个元素的引用可以用front()成员函数,如果容器为空,则行为未定义
vector支持用下标访问元素,类似数组一样c
其中n是一个无符号整数,如果n大于容器的长度,那么行为未定义
vector为了防止越界访问,其中有成员函数c.at(n),返回下标为n的元素的引用。如果下标越界,那么抛出out_of_range的异常
以上返回引用会根据容器的类型来判断,也就是说如果声明了一个const容器,那么返回一个const的引用,否则返回一个普通的引用
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve={1,2,3,4}; ve.back()=5;//4变成5 ve.front()=0;//1变成0 cout<<ve[0]<<" "<<ve[ve.size()-1]<<endl; try { ve.at(6); } catch(out_of_range &e) { cerr<<e.what()<<endl; } return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
删除元素
vector当中删除元素通常使用pop_back()用来删除最后一个元素,也可以使用erase()成员函数来删除任意位置的元素。由于vector的性质,删除任意位置的元素会降低效率。pop_back()成员函数用来删除vector中的最后一个元素,如果容器为空会出现未定义行为。
c.erase(it)成员函数,删除迭代器it所指向的元素,返回一个指向被删除元素之后的迭代器,如果it指向最后一个元素,那么返回以为尾后迭代器(通常是end())。若it就是end(),那么行为未定义。
c.erase(beg,ed)删除[beg,ed)范围的元素,同时返回最后一个元素的后面的迭代器,如果ed就是尾后迭代器,那么还返回一个尾后迭代器。
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve={1,2,3,4}; ve.erase(ve.begin(),ve.begin()+1);//删除第一个元素,结果是2 3 4 ve.erase(ve.begin());//删除第一个元素,结果是3 4 ve.pop_back();//删除最后一个元素,结果变成3 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
注意,删除元素以后会使迭代器失效,所以循环删除元素这样写是错误的。
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve={1,2,3,4,5,6,7,8,9}; for(auto it=ve.begin();it!=ve.end();it++)//错误写法 if((*it)%2)//删除奇数的元素 ve.erase(it); return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
正确的删除方式
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve={1,2,3,4,5,6,7,8,9}; for(auto it=ve.begin();it!=ve.end();)//没毛病 { if((*it)%2) ve.erase(it); else it++; } for(auto x:ve) cout<<x<<endl; return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
谨记,改变容器的长度的操作会使迭代器实效。
vector的容量与内存管理:
vector是一个可以增长长度的容器,用户可以多自行改变容器长度。C++11标准当中为了节约内存,新增加了一个函数用来回收多余的内存。size()成员函数,返回容器当中元素的个数。
resize(n)成员函数,重新定义容器的元素的个数。注意,不是容量。如果n大于当前的size(),那么多出的长度用默认值补充,如果n小于当前的size()那么只留下前n个元素,剩下的删除。
resize(n,t)成员函数,原则同上,只不过多出的元素用t补充而已
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve={1,2,3,4,5,6,7,8,9}; cout<<ve.size()<<endl;//9 ve.resize(15); cout<<ve.size()<<endl;//15 多出来的用默认值补充 ve.resize(3);//就留下前3个数,剩下的都删除 ve.resize(10,-1)//多出的元素用-1补充 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
capacity()成员函数是不重新分配内存的情况下,容器可以保存多少元素
reserve(n)成员函数是分配智商能容纳n个元素的内存空间。比如ve当中有三个元素,现在使用ve.reserve(1),那么没有变化。如果使用ve.reserve(10),那么ve.size()还是原来的值,不过ve.capacity()会变成10
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve; for(int i=0;i<100;i++)//放入100个元素 ve.push_back(i); cout<<ve.size()<<endl;//100 cout<<ve.capacity()<<endl;//128 ve.reserve(99);//比100小,所以什么也不做 cout<<ve.size()<<endl;//100 cout<<ve.capacity()<<endl;//128 ve.reserve(150); cout<<ve.size()<<endl;//100 cout<<ve.capacity()<<endl;//150 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
最后说一下C++11的新函数,shrink_to_fit()说简单点,就是把多余没用到的空间回收一下,这个函数是向系统提交一个申请,而不是一个强制命令。
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<int> ve; for(int i=0;i<100;i++)//放入100个元素 ve.push_back(i); cout<<ve.size()<<endl;//100 cout<<ve.capacity()<<endl;//128 ve.shrink_to_fit(); cout<<ve.size()<<endl;//100 cout<<ve.capacity()<<endl;//100 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
可能会觉得,那每次使用vector后都调用这个函数,不就很省内存了嘛。此言差矣,如果需要再向容器当中塞东西,那么就要重新给容器增加分配的内存,更浪费时间和效率。
多维vector的用法:
用二维向量做例子,所谓多维向量,就是一个向量一面存储的内容是一个向量。通常可以代替二维数组来使用。也可以使用向量的数组来表示。
二维向量的声明:
vector<vector<int>> vve;//在新版本的编译器当中这样写没问题 vector<vector<int> >vvve;//旧版本的编译器会把两个相连的尖括号当成流操作1
2
初始化:
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<vector<int>> vva(10,vector<int>(0));//vve里面放10个vector,每个vector里面初始化一个0元素 vector<vector<int>> vvb(10,{1,2,3,4});//v里面放10个vector,每个vector用列表初始化 vector<int> va={0,2,3}; vector<vector<int>> vvc(3,va);//vvc中里面放入3个va return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
总之,和初始化一个普通的vector没什么两样,只不过就是初始化的元素变成了vector而已
插入:
这里使用push_back和insert#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<vector<int>> vvb(4,{1,2,3,4}); for(int i=0;i<3;i++) { vector<int> vtemp={i}; vvb.push_back(vtemp);//每次塞入一个用i初始化过的vtemp } vector<int> vt={666}; vvb.insert(vvb.begin(),vt);//把vt放在vvb的头部 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
遍历:
一般使用下标遍历、迭代器遍历#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<vector<int>> vvb(4,{1,2,3,4}); for(int i=0;i!=vvb.size();i++) { for(int j=0;j!=vvb[i].size();j++) cout<<vvb[i][j]<<" "; cout<<endl; } for(auto bit=vvb.begin();bit!=vvb.end();bit++) { for(auto bbit=bit->begin();bbit!=bit->end();bbit++)//bit类似指向一个vvb[i],里面是向量中的元素 cout<<*bbit<<" "; cout<<endl; } return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
当然,也可以用C++11的新遍历方式
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<vector<int>> vvb(4,{1,2,3,4}); for(auto x:vvb) { for(auto xx:x) cout<<xx<<" "; cout<<endl; } return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
删除操作:
和一维vector删除元素没什么区别#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); vector<vector<int>> vvb(4,{1,2,3,4}); vvb.pop_back();//删除最后一个元素 vvb.erase(vvb.begin());//删除第一个向量元素 return 0; }1
2
3
4
5
6
7
8
9
10
11
三维,或者更高维的也是如此。
容器的指针,vector::data:
调用vector::data会返回一个指向第一个元素的指针,由于vector的内存是连续的,所以指针的移动,也就对应vector当中的元素遍历。看代码#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); std::vector<int> myvector (5); int* p = myvector.data(); *p = 10; ++p; *p = 20; p[2] = 100; cout << "myvector contains:"; for(unsigned i=0; i<myvector.size(); ++i) cout << ' ' << myvector[i]; cout << '\n'; return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
结果是myvector contains: 10 20 0 100 0
上面的代码来自http://www.cplusplus.com/reference/vector/vector/data/
小结:
vector的成员函数当中还有一个get_allocate,是获取整块内存类似,c语言当中memcpy函数。这里先不介绍,后面补上。 vector在C++代码当中使用十分广泛,可以用来表示矩阵,表示向量,当做邻接表来使用,十分方便。
加上与泛型算法和模板的搭配,使用功能也非常强大。
现在就总结这些,在编程和学习当中如果遇到有关vector的问题,还会在这里继续补充。如果写出来的观点和代码有什么问题,请各位看官不吝指正,我会及时更改。
to be continue~
相关文章推荐
- STL priority_queue<> 用法 <转>
- STL algorithm accumulate vector<string> demo
- 11/7/2 STL vector<T> (2)
- <stack>,<queue>,<vector>的用法
- STL容器之vector<bool>
- C++ vector 用法(#include <vector>)
- STL vector<bool>中的swap方法(4)
- STL priority_queue<> 用法
- #include<vector> 的用法
- STL:vector<bool> 和bitset
- Effective STL 学习笔记 Item 18: 慎用 vector<bool>
- STL vector<bool>总结
- STL vector<bool>的介绍(1)
- hdu 4941 stl的map<node,int>用法
- stl--<map>的用法
- c++中vector<int>和vector<int*>的用法比较
- #include<vector> 的用法
- STL hash<vector<bool>>
- &lt;Effective STL&gt;笔记--vector和string
- STL vector<bool>中的flip方法(3)