转载自:https://blog.csdn.net/hhu1506010220/article/details/51971642
介绍
这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作。本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if()和for_each()中的使用。通过阅读这篇文章读者应该能够有效地使用vector容器,而且应该不会再去使用C类型的动态数组了。
Vector总览
vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。
Vector成员函数:
函数 | 表述 |
c.assign(begin,end)
c.assign(n,element) | 截取c中[begin,end)区间的元素赋值给c
截取c中n个element的拷贝赋值给c |
c.at(idx) | 传回索idx所指的数据,如果idx越界,抛出out_of_range |
c.back() | 传回最后一个数据,不检查这个数据是否存在 |
c.begin() | 传回迭代器中的第一个数据 |
c.capacity() | 返回容器中的数据个数 |
c.clear() | 移除容器中的所有数据 |
c.empty() | 判断容器是否为空 |
c.end() | 传回迭代器中的最后一个数据地址 |
c.earse(pos)
c.earse(begin,end) | 删除pos位置的数据,传回下一个数据的位置
删除[begin,end)区间的数据,传回下一个数据的位置 |
c.front() | 传回第一个数据 |
get_allocator | 使用构造函数返回一个拷贝 |
c.insert(pos,element)
c.insert(pos,n,element) c.insert(pos,begin,end) | 在pos位置插入一个element拷贝,传回新数据位置
在pos位置插入n个element数据,无返回值 在pos位置插入【begin,end)区间的数据,无返回值 |
c.max_size() | 返回容器中最大数据的数量 |
c.pop_back() | 删除最后一个数据 |
c.push_back(element) | 在尾部加入一个数据 |
c.rbegin() | 传回一个逆向队列的第一个数据 |
c.rend() | 传回一个逆向队列的最后一个数据的下一个位置 |
c.resize(num) | 重新制定队列的长度 |
c.reserve()
c.size() c1.swap(c2) swap(c1,c2) vector vector vector vector vector c.~vector | 保留适当的容量
返回容器中实际数据的个数 将c1和c2元素互换 同上 创建一个空的vector 复制一个vector 创建一个vector,含有n个元素,数据均已缺省构造产生 创建一个含有n个element拷贝的vector 创建一个以【begin,end)区间的vector 销毁所有数据,释放内存 |
创建一个Vector
1 vector<string> strv(500);//创建包含500个string类型数据的vector
2 vector<string> strv1(500,"0");//创建包含500个string类型数据的vector,并初始化为"0"
3 vector<string> strv2(strv);
向vector添加一个数据
vector添加数据的缺省方法是push_back()。push_back()函数表示将数据添加到vector的尾部,并按需要来分配内存。
1 vector<int> veri;
2 for(int i=0;i<10;i++){
3 veri.push_back(i);
4 }
获取vector中指定位置的数据
很多时候我们不必要知道vector里面有多少数据,vector里面的数据是动态分配的,使用push_back()的一系列分配空间常常决定于文件或一些数据源。如果你想知道vector存放了多少数据,你可以使用empty()。获取vector的大小,可以使用size()。例如,如果你想获取一个vector v的大小,但不知道它是否为空,或者已经包含了数据,如果为空想设置为-1,你可以使用下面的代码实现:
1 int nSize = v.empty() ? -1 : static_cast<int>(v.size());
访问vector中的数据
使用两种方法来访问vector。
1、vector::at()
2、vector::operator[]
operator[]主要是为了与C语言进行兼容。它可以像C语言数组一样操作。但at()是我们的首选,因为at()进行了边界检查,如果访问超过了vector的范围,将抛出一个例外。由于operator[]容易造成一些错误,所有我们很少用它,下面进行验证一下:
1 vector<int> v;
2 v.reserve(10);
3 for(int i=0; i<7; i++)
4 v.push_back(i);
5 try
6 {
7 int iVal1 = v[7]; // not bounds checked - will not throw
8 int iVal2 = v.at(7); // bounds checked - will throw if out of range
9 }
10 catch(const exception& e)
11 {
12 cout << e.what();
13 }
我们使用reserve()分配了10个int型的空间,但并不没有初始化。
你可以在这个代码中尝试不同条件,观察它的结果,但是无论何时使用at(),都是正确的。
删除vector中的数据vector能够非常容易地添加数据,也能很方便地取出数据,同样vector提供了erase(),pop_back(),clear()来删除数据,当你删除数据的时候,你应该知道要删除尾部的数据,或者是删除所有数据,还是个别的数据。在考虑删除等操作之前让我们静下来考虑一下在STL中的一些应用。压缩一个臃肿的vcetor
很多时候大量的删除数据,或者通过使用reserve(),结果vector的空间远远大于实际需要的。所有需要压缩vector到它实际的大小。resize()能够增加vector的大小。Clear()仅仅能够改变缓存的大小,所有的这些对于vector释放内存等九非常重要了。如何来解决这些问题呢,让我们来操作一下。
我们可以通过一个vector创建另一个vector。让我们看看这将发生什么。假定我们已经有一个vector veri,它的内存大小为1000,当我们调用size()的时候,它的大小仅为7。我们浪费了大量的内存。让我们在它的基础上创建一个vector。
1 vector<int> veri;
2 veri.reserve(1000);
3 for(int i=0;i<10;i++){
4 veri.push_back(i);
5 }
6
7 cout << "veri capacity="<<veri.capacity()<<endl;
8 cout << "veri size="<<veri.size()<<endl;
创建新的vector来与veri交换
1 vector<int> veri;
2 veri.reserve(1000);
3 for(int i=0;i<10;i++){
4 veri.push_back(i);
5 }
6
7 cout << "veri capacity="<<veri.capacity()<<endl;
8 cout << "veri size="<<veri.size()<<endl;
9
10 vector<int> veri1;
11 veri1.swap(veri);
12
13 cout << "veri capacity="<<veri.capacity()<<endl;
14 cout << "veri size="<<veri.size()<<endl;
15 cout << "veri1 capacity="<<veri1.capacity()<<endl;
16 cout << "veri1 size="<<veri1.size()<<endl;
新的vector的capacity变为1000,size为7。这样让没有达到目的
1 vector<int> veri;
2 veri.reserve(1000);
3 for(int i=0;i<10;i++){
4 veri.push_back(i);
5 }
6
7 cout << "veri capacity="<<veri.capacity()<<endl;
8 cout << "veri size="<<veri.size()<<endl;
9
10 // vector<int> veri1;
11 // veri1.swap(veri);
12 vector<int>(veri1).swap(veri1);
13 cout << "veri capacity="<<veri.capacity()<<endl;
14 cout << "veri size="<<veri.size()<<endl;
15 // cout << "veri1 capacity="<<veri1.capacity()<<endl;
16 // cout << "veri1 size="<<veri1.size()<<endl;
达到目的!!!!
同时,测试一下clear,可以发现clear只是把内容清楚了,但vector的内存并没有释放掉,可以使用swap来彻底释放内存。
1 vector<int> veri;
2 veri.reserve(1000);
3 for(int i=0;i<10;i++){
4 veri.push_back(i);
5 }
6 cout << "veri capacity="<<veri.capacity()<<endl;
7 cout << "veri size="<<veri.size()<<endl;
8
9 veri.clear();
10 cout << "veri capacity="<<veri.capacity()<<endl;
11 cout << "veri size="<<veri.size()<<endl;
12 vector<int>(veri).swap(veri);
13 cout << "veri capacity="<<veri.capacity()<<endl;
14 cout << "veri size="<<veri.size()<<endl;
迭代器
1 // vector.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include <vector>
6 #include <iostream>
7 #include <numeric>
8 #include <algorithm>
9 using namespace std;
10
11 bool Comp(const int &a,const int &b){
12
13 return a > b;
14 }
15 int _tmain(int argc, _TCHAR* argv[])
16 {
17 vector<int> vec;
18 //添加元素
19 vec.push_back(10);
20 vec.push_back(20);
21 vec.push_back(30);
22
23 vector<int>::iterator vecit ;
24 cout << "初始:";
25 for(vecit=vec.begin();vecit!=vec.end();vecit++){
26 cout << *vecit << " ";
27 }
28 cout <<endl;
29
30 cout << "求和="<<accumulate(vec.begin(),vec.end(),0)<<endl;
31
32 //插入
33 vec.insert(vec.begin(),1);
34 vec.insert(vec.end(),40);
35 cout << "插入:";
36 for(vecit=vec.begin();vecit!=vec.end();vecit++){
37 cout << *vecit << " ";
38 }
39 cout <<endl;
40 //删除
41 vec.erase(vec.begin());
42 cout << "删除:";
43 vec.erase(vec.begin(),vec.begin()+2);
44 for(vecit=vec.begin();vecit!=vec.end();vecit++){
45 cout << *vecit << " ";
46 }
47 cout <<endl;
48 //转置
49 reverse(vec.begin(),vec.end());
50 cout << "转置:";
51 for(vecit=vec.begin();vecit!=vec.end();vecit++){
52 cout << *vecit << " ";
53 }
54 cout <<endl;
55
56 vec.push_back(1);
57 vec.push_back(3);
58 vec.push_back(2);
59 vec.push_back(55);
60 vec.push_back(-1);
61 vec.push_back(0);
62 vec.push_back(2);
63 vec.push_back(3);
64 vec.push_back(4);
65 cout << "初始:";
66 for(vecit=vec.begin();vecit!=vec.end();vecit++){
67 cout << *vecit << " ";
68 }
69 cout <<endl;
70 sort(vec.begin(),vec.end());
71 cout << "升序:";
72 for(vecit=vec.begin();vecit!=vec.end();vecit++){
73 cout << *vecit << " ";
74 }
75 cout <<endl;
76 sort(vec.begin(),vec.end(),Comp);
77 cout << "降序:";
78 for(vecit=vec.begin();vecit!=vec.end();vecit++){
79 cout << *vecit << " ";
80 }
81 cout <<endl;
82 return 0;
83 }
字符串
输入:
1 #include "stdafx.h"
2 #include <string>
3 #include <iostream>
4
5 using namespace std;
6 int _tmain(int argc, _TCHAR* argv[])
7 {
8 string s1;
9 s1 = "hello";
10 string s2;
11 char s[1024];
12 //scanf输入速度比cin快的多,但是scanf是c函数,不能输入字符串
13 scanf("%s",s);
14 s2 = s;
15 cout << s1 << endl;
16 cout << s2 << endl;
17 return 0;
18 }
尾部添加字符字符串直接用+号 例如: s += 'a'; s += "abc",或者使用append方法,s.append(“123”)
删除:
1 // string1.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include <string>
6 #include <iostream>
7
8 using namespace std;
9 int _tmain(int argc, _TCHAR* argv[])
10 {
11 string s;
12 s = "0123456789";
13
14 string::iterator it = s.begin();
15 s.erase(it+3);//删除s[3]
16 cout << s << endl;
17
18 s = "0123456789";
19 s.erase(it+1,it+4);//删除s[1]-s[3]
20 cout << s << endl;
21
22 s.clear();
23 cout << s << endl;
24 }
查找(find)
用find找到string里面第一个要找到元素(char或者串),找到返回数组下标,找不到返回end()迭代器
string和vector有很多相同的东西,比如length(),size(),empty(),reverse(),相对也容易,就不一一说了。
数字化处理(string)
经常会遇到这样一种情况,有一个数字,需要把每一位给提取出来,如果用取余数的方法,花费的时间就会很长,所以可以当成字符串来处理,方便、省时。
例子:求一个整数各位数的和
1 // string1.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include <string>
6 #include <iostream>
7
8 using namespace std;
9 int _tmain(int argc, _TCHAR* argv[])
10 {
11 string s;
12 s = "0123456789";
13 int sum = 0;
14 for(int i=0;i<s.size();i++){
15 switch(s.at(i)){
16 case '1':sum+=1;break;
17 case '2':sum+=2;break;
18 case '3':sum+=3;break;
19 case '4':sum+=4;break;
20 case '5':sum+=5;break;
21 case '6':sum+=6;break;
22 case '7':sum+=7;break;
23 case '8':sum+=8;break;
24 case '9':sum+=9;break;
25 }
26 }
27 cout << "sum =" << sum << endl;
28
29 }
string与数值相互转换( sprintf
1 // string1.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include <string>
6 #include <iostream>
7 #include <sstream>
8 using namespace std;
9
10 string converToString(double x){
11 ostringstream o;
12 if(o << x){
13 return o.str();
14 }
15 return "error";
16 }
17
18 double converFromString(string s){
19 istringstream i(s);
20 double x;
21 if(i >> x){
22 return x;
23 }
24 return 0.0;
25 }
26 int _tmain(int argc, _TCHAR* argv[])
27 {
28 char b[100];
29 sprintf(b,"%d",1987);
30 string s1 ;
31 s1 = b;
32 cout << s1 << endl;
33
34 string s2 = converToString(1954);
35 cout << s2 << endl;
36
37 string s3 = "202";
38 int c = converFromString(s3);
39 cout << c << endl;
40
41 string s4 = "casacsa6";
42 int d = converFromString(s4);
43 cout << d << endl;
44
45 string s5 = "31af12";
46 int f = converFromString(s5);
47 cout << f << endl;
48 return 0;
49 }
set容器
set是用红黑树的平衡二叉索引树的数据结构来实现的,插入时,它会自动调节二叉树排列,把元素放到适合的位置,确保每个子树根节点的键值大于左子树所有的值、小于右子树所有的值,插入重复数据时会忽略。set迭代器采用中序遍历,检索效率高于vector、deque、list,并且会将元素按照升序的序列遍历。set容器中的数值,一经更改,set会根据新值旋转二叉树,以保证平衡,构建set就是为了快速检索(python中的set一旦建立就是一个常量,不能改的)。
multiset,与set不同之处就是它允许有重复的键值。
set的正反遍历:迭代器iterator,reverse_iterator
1 // string1.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include <set>
6 #include <iostream>
7
8 using namespace std;
9
10 int _tmain(int argc, _TCHAR* argv[])
11 {
12 set<int> v;
13 v.insert(1);
14 v.insert(5);
15 v.insert(7);
16 v.insert(2);
17 v.insert(4);
18 v.insert(10);
19
20 //中序遍历=升序遍历
21 set<int>::iterator it;
22 for(it=v.begin();it!=v.end();it++){
23 cout << *it << " ";
24 }
25 cout << endl;
26
27 set<int>::reverse_iterator it1;
28 for(it1=v.rbegin();it1!=v.rend();it1++){
29 cout << *it1 << " " ;
30 }
31 cout << endl;
32
33 return 0;
34 }
自定义比较函数,insert的时候,set会使用默认的比较函数(升序),很多情况下需要自己编写比较函数。
1、如果元素不是结构体,可以编写比较函数,下面这个例子是用降序排列的(和上例插入数据相同):
1 // string1.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include <set>
6 #include <iostream>
7
8 using namespace std;
9
10
11 struct Comp
12 {
13 //重载()
14 bool operator()(const int &a, const int &b)
15 {
16 return a > b;
17 }
18 };
19 int _tmain(int argc, _TCHAR* argv[])
20 {
21 set<int> v;
22 v.insert(1);
23 v.insert(5);
24 v.insert(7);
25 v.insert(2);
26 v.insert(4);
27 v.insert(10);
28
29 //中序遍历=升序遍历
30 set<int>::iterator it;
31 for(it=v.begin();it!=v.end();it++){
32 cout << *it << " ";
33 }
34 cout << endl;
35
36 set<int,Comp>::reverse_iterator it1;
37 for(it1=v.rbegin();it1!=v.rend();it1++){
38 cout << *it1 << " " ;
39 }
40 cout << endl;
41
42 return 0;
43 }
map
map也是使用红黑树,他是一个键值对(key:value映射),便利时依然默认按照key程序的方式遍历,同set。
multimap
multimap由于允许有重复的元素,所以元素插入、删除、查找都与map不同。
插入insert(pair(value1,value2))
至于删除和查找,erase(key)会删除掉所有key的map,查找find(key)返回第一个key的迭代器
deque
deque和vector一样,采用线性表,与vector唯一不同的是,deque采用的分块的线性存储结构,每块大小一般为512字节,称为一个deque块,所有的deque块使用一个Map块进行管理,每个map数据项记录各个deque块的首地址,这样以来,deque块在头部和尾部都可已插入和删除元素,而不需要移动其它元素。使用push_back()方法在尾部插入元素,使用push_front()方法在首部插入元素,使用insert()方法在中间插入元素。一般来说,当考虑容器元素的内存分配策略和操作的性能时,deque相对vectore更有优势。
(下面这个图,我感觉Map块就是一个list< map
插入删除
遍历当然可以使用下标遍历,在这里使用迭代器。
list
list
插入:push_back尾部,push_front头部,insert方法前往迭代器位置处插入元素,链表自动扩张,迭代器只能使用++--操作,不能用+n -n,因为元素不是物理相连的。
遍历:iterator和reverse_iterator正反遍历
删除:pop_front删除链表首元素;pop_back()删除链表尾部元素;erase(迭代器)删除迭代器位置的元素,注意只能使用++--到达想删除的位置;remove(key) 删除链表中所有key的元素,clear()清空链表。
查找:it = find(l.begin(),l.end(),key)
排序:l.sort()
删除连续重复元素:l.unique() 【2 8 1 1 1 5 1】 --> 【 2 8 1 5 1】
bitset
从来没用过,上两幅图吧就:
stack(后进先出)
这个印象深刻,学数据结构的时候做表达式求值的就是用的栈。
原文链接: https://www.cnblogs.com/zhangjxblog/p/8902010.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/272709
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!