关于本文
这一篇主要介绍序列容器(vector、deque、list、array)
,会在每种序列容器介绍开始前,说明一下优点以及和其他三种的对比和使用场景
array
这个就不详细展开说了,说白了就是一个数组,详情参考下面这篇博客,博主写的很详细,点我看array。
vector
1.1 是什么
vector和array相似,其区别在于vector 的大小可以自动增长,从而可以包含任意数量的元素,只要元素个数超出 vector 当前容量,就会自动分配更多的空间。其只能在容器尾部高效地删除或添加元素,在其他地方进行插入删除操作可能会非常慢,只要不是特殊情况,我们都会选择vector来进行操作。
1.2 为什么
可变大小数组。支持快速随机访问。在尾部插入数据元素非常快。
1.3 常用的操作
1.3.1 初始化
下面是一个生成存放 double 型元素的 vector示例:
vector<double> values;
这些个容器的初始化都是这么个套路(为的是快速上手,如果想细细地研究原理的话,可以先参考其他博主的博文,有时间慢慢更),大家使用多了就上手了。
1.3.2 插入、遍历等常用操作
插入
1.因为容器中没有元素,所以没有分配空间,当添加第一个数据项时,会自动分配内存。可以像下面这样通过调用 reserve() 来增加容器的容量:
values.reserve(20);
注意:不是reverse反转函数,千万别拼错了,我就是因为念的顺嘴,写了个reverse。
这样就设置了容器的内存分配,至少可以容纳 20 个元素。如果当前的容量已经大于或等于 20 个元素,那么这条语句什么也不做。调用 reserve() 并不会生成任何元素。values 容器这时仍然没有任何元素,直到添加了 20 个元素后,才会分配更多的内存。调用 reserve() 并不会影响现有的元素。当然,如果通过调用 reserve() 来增加内存,任何现有的迭代器,例如开始迭代器和结束迭代器,都会失效,所以需要重新生成它们。 所以还是要充分发挥可变长度数组的优势。
2.循环进行插入元素,并输出。
#include<iostream>
#include<vector>
using namespace std;
void show(vector<int> values){
int i;
for(i=0;i<values.size();i++){ //可以使用迭代器vector<int>::iterator it;来进行遍历。
cout<<values[i]<<" ";
}
cout<<endl;
}
int main(){
int a;
vector<int> v;
while(cin>>a){
if(a==0)
break;
else
v.push_back(a);//循环向vector中添加元素,相当于栈,把元素压入栈。
}
show(v);
return 0;
}
遍历
上面的输出使用的,一般性的数组的循环输出的方式,我们还可以使用迭代器进行输出,比如:
vector<int> values)
vector<int>::iterator it;
for(it=values.begin();it!=values.end();it++){
cout<<*it<<" ";
}
效果是一样的。
常用操作
下面利用代码+注释,介绍一些vector常用的方法。
v.insert(v.begin()+2,10);//在第二个元素后面插入10,begin表示的是第一个元素要和数组的a[0]区分开。
v.insert(c.begin()+1, 2, vla);//从第二个位置开始,连续插入两个元素
v.insert(c.begin()+2, c.begin(), c.end() - 1);//从第三个位置开始,连续插入第一个位置到最后一个位置之间的元素
cout<<v[2]<<endl;//输出第三个元素,v[0]、v[1]、v[2]。
v.erase(v.begin()+1);//删除第二个元素
v.erase(v.begin()+1,v.end()+3);//删除区间[1,3-1]之间的元素,区间从0开始。
v.size();//vector的大小。
v.clear();//清空vector。
v.begin();//返回第一个元素的迭代器。
v.front();//返回第一个元素。
v.end();//返回最后一个元素的迭代器。
v.back();//返回最后一个元素。
swap(v,v1);//交换两个vector的内容,前提是这两个vector的元素类型必须相同。
v.pop_back();//弹出最后一个元素。
这里面还有一个点比较有用,今天做题遇到的,就是vector种的pair,也是一个键值对,操作比较方便。把题和代码贴出来,大家有兴趣的可以试一下。
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
typedef struct Student{
string name;
int no;//学号
vector<float> score;
}student;
student s[10001];
vector<pair<int,int> > fail_stu;//pair键值,其中第一个表示的是学生的数组下标,第二个表示的是挂的科目数
bool cmp(pair<int,int>a,pair<int,int>b){
return a.second>b.second;
}
int main(){
int i,j,cnt=0;
float scor;
cout<<"请输入姓名、学号、成绩:"<<endl;
while(cin>>s[cnt].name){
if(s[cnt].name == "0"){
break;
}
cin>>s[cnt].no;
while(cin>>scor){
if(scor == -1)
break;
s[cnt].score.push_back(scor);
}
cout<<s[cnt].score[1]<<endl;
cnt++;
cout<<"请继续输入,按ctrl+z结束输入:"<<endl;
}
cout<<"不及格课程大于两门的学生的学号及平均分:"<<endl;
for(i=0;i<cnt;i++){
//fail_stu.clear();
int flag=0;
float total=0,avg=0;
for(j=0;j<s[i].score.size();j++){
if(s[i].score[j]<60.0)
flag++;
total+=s[i].score[j];
}
fail_stu.push_back(make_pair(i,flag));
if(flag > 2){
avg=total/s[i].score.size();
cout<<s[i].no<<":"<<avg<<endl;
}
}
cout<<"按照不及格课程数来排序:"<<endl;
sort(fail_stu.begin(),fail_stu.end(),cmp);
for(i=0;i<fail_stu.size();i++){
cout<<s[fail_stu[i].first].no<<":"<<fail_stu[i].second<<endl;
}
return 0;
}
大家上代码实操就会懂的。
deque
1.1 是什么
它以双端队列的形式组织元素。可以在容器的头部和尾部高效地添加或删除对象,这是它相对于 vector 容器的优势。当需要这种功能时,可以选择这种类型的容器。
无论何时,当应用包含先入先出的事务处理时,都应该使用 deque 容器。处理数据库事务或模拟一家超市的结账队列,像这两种应用都可以充分利用 deque 容器。
1.2 为什么
双端队列。支持快速快速访问。在头尾位置插入/删除速度很快,主要就是头、尾插入,设想输入1、输出1,输入2、输出2 1,输入3、输出3 2 1;就是类似这种操作的时候可以用deque。
1.3 常用的操作
1.3.1 初始化
直接使用默认的构造器初始化,其中没有任何元素。
deque<int> a_deque;
可以在初始化的时候规定元素的个数。
deque<int> my_deque(10);
可以直接给定元素值。
deque<string> words { "one", "none", "some", "all", "none","most", "many” };
1.3.2 插入、遍历等常用操作
插入
插入分为头插、尾插:
deque<string> dec;
dec.push_back("hello"); //尾部插入
dec.push_front("world"); //头部插入
除了和 vector —样都有 emplace_back() 函数外,deque 还有成员函数 emplace_front(),可以在序列的开始位置生成新的元素。和 vector 一样,也可以使用 emplace() 或 insert() 在 deque 内部添加或移除元素。这个过程相对要慢一些,因为这些操作需要移动现有的元素。
关于 vector 容器的所有 insert() 函数也同样适用于 deque 容器。
遍历
遍历访问和vector差不多,基本一样。这里说一下deque支持[]以及可以使用deque.at(元素的下标)
来访问元素。
常用操作
dec.pop_back(); //尾部删除
dec.pop_front(); //头部删除
//erase操作
dec.erase(dec.begin());
dec.erase(dec.end()-3, dec.end());
dec.back(); //返回最后一个元素
dec.front(); //返回第一个元素
dec.empty(); //判断是否为空
//调整容器大小,不足以参数2补充
dec.resize(5);
dec.resize(5,"hello");
dec.size(); //容器大小
deque<string> s_dec;
swap(s_dec, dec); //交换容器内容
s_dec.swap(dec); //交换容器内容
dec.clear(); //清空
//反序输出
deque<string>::reverse_iterator rit;
for(rit = dec.rbegin(); rit != dec.rend(); ++rit)
{
cout<<*rit<<endl;
}
list
1.1 是什么
list容器模板定义在 list 头文件中,是某一类型对象的双向链表。
1.2 为什么
ist 容器具有一些 vector 和 deque 容器所不具备的优势,它可以在常规时间内,在序列已知的任何位置插入或删除元素。这是我们使用 list,而不使用 vector 或 deque 容器的主要原因。
1.3 常用的操作
1.3.1 初始化
list 容器的构造函数的用法类似于 vector 或 deque 容器。下面这条语句生成了一个空的 list 容器:
list<string> words;
可以创建一个带有给定数量的默认元素的列表:
list<string> sayings {20};
生成一个包含给定数量的相同元素的列表:
list<double> values(50, 3.14159265);
list 容器有一个拷贝构造函数,因此可以生成一个现有 list 容器的副本:
list<double> save_values {values};
可以用另一个序列的开始和结束迭代器所指定的一段元素,来构造 list 容器的初始化列表:
list<double> samples {++cbegin(values), --cend(values)};
1.3.2 插入、遍历等常用操作
关于常用操作这个博主写的非常详细
等遇到list的问题我再详细更list。
最后关于容器的选择
- 强调快速随机访问。则vector要比list好得多 。
- 已知要存储元素的个数。vector 好于list。
- 强调增删且不要在两端插入修改元素。则list显然要比vector好。
- 除非我们需要在容器首部插入和删除元素,deque好于vector。因为vector仅仅在尾部增删快速。
- 如果只需要在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑输入时将元素读入到一个List容器,然后排序,然后将排序后的list容器复制到一个vector容器中。
- 如果只在容易的首部和尾部插入数据元素,则选择deque.