前期简介:STL的六个组件
容器(container):一种空间,用来存放数据,可以分为连续空间和非连续空间
算法(algorithm):用来操作数据
迭代器(iterator):泛化的指针,用来指向容器中的某个位置便于算法间接的操作数据
仿函数(functor):又叫函数对象,即重载了函数调用运算符的类其实例化的对象,为算法提供策略(抛开函数调用来操作数据外,还可以使用仿函数来调用数据)
适配器(adaptor):为一个类模板,用于接口映射
空间配置器(allocator):为容器分配所需要大小的空间
容器的分类
STL标准模板库提供了很多容器,总体分为两种容器,序列容器(sequence containers)和关联容器(associative containers)
常见的序列容器有vector、deque、queue、stack、list,
常见的关联容器有set、multiset、map、multimap。
上章节讲到了vector容器,这章节详细的讲一下deque容器
deque
deque区别于vector,他是双端动态数组(双端队列),即可以两端操作,
deque的特点
1、deque容器允许以常数项时间对头尾两端进行操作,比如插入删除操作
2、deque容器没有容量的概念(capacity),因为deque的容器是分段连续的空间,可以理解为多个vector且头尾都可以操作的空间集合(不严谨),有图示下:
此图可知:这个缓冲区每一行可以理解为一个vector,每个vector通过中控器相连在一起形成一个deque容器,类似于链表(链表中的每一个节点通过指针相连)
deque的API
/*
* deque的构造函数
* deque<T> deque;//默认构造函数
* deque(begin,end);//将[begin,end)区间的数据拷贝给自己,不能这样初始化,可以使用insert(deque,begin,end);
* deque(num,element);//将num个element数据拷贝给自己,调用不适用,可以使用deque(deque.end(),num,element);
* deque(cons deque &deque);//拷贝构造函数,在deque不适合用(),可以使用=,即deque(自己)=deque;
*
* deque的赋值操作
* assign(begin,end);//通过调用assign方法将[begin,end)区间的数据拷贝给自己
* assign(num,element);//通过调用assign方法将num个element数据拷贝给自己
* deque& opeerator=(const deque &deque);//重载赋值运算符,将deque对象拷贝赋值给自己
* swap(deque);//调用swap方法将deque对象的数据和自己的数据互换
*
* deque空间操作
* deque.size();//返回容器中的元素个数
* deque.empty();//判断容器是否为空,为空则返回true,不为空返回false
* deque.resize(num);//重新指定容器的长度为num,若容器变长,比原来长的部分空间以默认值填充(一般是0),若容器变短,则超出的部分元素被删除
* deque.resize(num,element);//重新指定容器的长度为num,若容器变长,比原来长的部分空间以指定的element填充,若容器变短,则超出的部分元素被删除
*
* deque的双端队列插入和删除操作
* push_bakc(element);//尾部插入一个element数据
* push_front(element);//在头部插入一个element数据
* pop_back();//没有参数,调用一次pop_back方法则表示删除容器尾部最后一位元素
* pop_front();//也没有参数,调用一次pop_front方法则表示删除容器头部第一个元素
*
*/
#include <iostream>
#include<deque>
using namespace std;
//自定义一个遍历deque容器的方法
void printDequeInt(deque<int> &d){
deque<int>::iterator it=d.begin();
for(;it!=d.end();it++){
//*it表示取值,即*it==int(某一个值)
cout<<*it<<" ";
}
cout<<endl;//容器内所有数据遍历之后输出换行
}
void test(){
/*
* deque的构造函数
* deque<T> deque;//默认构造函数
* deque(begin,end);//将[begin,end)区间的数据拷贝给自己,不能这样初始化,可以使用insert(deque,begin,end);
* deque(num,element);//将num个element数据拷贝给自己,调用不适用,可以使用deque(deque.end(),num,element);
* deque(cons deque &deque);//拷贝构造函数,在deque不适合用(),可以使用=,即deque(自己)=deque;
*
* deque的赋值操作
* assign(begin,end);//通过调用assign方法将[begin,end)区间的数据拷贝给自己
* assign(num,element);//通过调用assign方法将num个element数据拷贝给自己
* deque& opeerator=(const deque &deque);//重载赋值运算符,将deque对象拷贝赋值给自己
* swap(deque);//调用swap方法将deque对象的数据和自己的数据互换
*
* deque空间操作
* deque.size();//返回容器中的元素个数
* deque.empty();//判断容器是否为空,为空则返回true,不为空返回false
* deque.resize(num);//重新指定容器的长度为num,若容器变长,比原来长的部分空间以默认值填充(一般是0),若容器变短,则超出的部分元素被删除
* deque.resize(num,element);//重新指定容器的长度为num,若容器变长,比原来长的部分空间以指定的element填充,若容器变短,则超出的部分元素被删除
*
* deque的双端队列插入和删除操作
* push_bakc(element);//尾部插入一个element数据
* push_front(element);//在头部插入一个element数据
* pop_back();//没有参数,调用一次pop_back方法则表示删除容器尾部最后一位元素
* pop_front();//也没有参数,调用一次pop_front方法则表示删除容器头部第一个元素
*
*/
//以int类型的deque容器为例,deque容器的双端队列插入操作
deque<int> deq;
deq.push_back(100);
deq.push_back(300);
deq.push_back(900);
printDequeInt(deq);
deq.push_front(1200);
deq.push_front(2000);
printDequeInt(deq);
//deque容器的删除和空间操作
cout<<deq.size()<<endl;//尾部删除前容器大小
deq.pop_back();//调用尾部删除方法之后,尾部元素被删除
cout<<deq.size()<<endl;//删除后容器大小
printDequeInt(deq);
cout<<deq.size()<<endl;//头部删除前容器大小
deq.pop_front();
cout<<deq.size()<<endl;//头部删除后容器大小
printDequeInt(deq);
if(deq.empty()){
cout<<"容器为空"<<deq.size()<<endl;
cout<<"尾部元素:"<<deq.back()<<"头部元素:"<<deq.front()<<endl;
}
else{
cout<<"容器非空,大小为:"<<deq.size()<<endl;
cout<<"尾部元素:"<<deq.back()<<"头部元素:"<<deq.front()<<endl;
}
deq.resize(100);
cout<<deq.size()<<endl;
printDequeInt(deq);
deq.resize(3);
cout<<deq.size()<<endl;
printDequeInt(deq);
//deque的赋值操作
deque<int> deq1;
deq1.assign(deq.begin(),deq.end());
cout<<deq.size()<<endl;
printDequeInt(deq1);
deq1.assign(9,9999);
cout<<deq1.size()<<endl;
printDequeInt(deq1);
deque<int> deq2;
cout<<deq2.size()<<endl;
deq2.resize(100);
deq2=deq1;
cout<<deq2.size()<<endl;
printDequeInt(deq2);
deq2.insert(deq2.end(),4,888);
cout<<deq2.size()<<endl;
printDequeInt(deq2);
deq2.insert(deq2.end(),deq1.begin(),deq1.end());
cout<<deq2.size()<<endl;
printDequeInt(deq2);
deq2.swap(deq1);
cout<<"deq2的大小:"<<deq2.size()<<" deq1的大小:"<<deq1.size()<<endl;
printDequeInt(deq2);
printDequeInt(deq1);
/*
deque<int> deq2;
cout<<deq2.size()<<endl;
deq2.resize(100);
deq2(deq1);
cout<<deq2.size()<<endl;
printDequeInt(deq2);
deq2(4,888);
cout<<deq2.size()<<endl;
printDequeInt(deq2);
deq2(deq1.begin(),deq1.end());
cout<<deq2.size()<<endl;
printDequeInt(deq2);
deq2.swap(deq1);
cout<<deq2.size()<<deq1.size()<<endl;
printDequeInt(deq2);
printDequeInt(deq1);
*/
}
int main()
{
test();
return 0;
}
输出结果:
tips:deq的构造函数不能直接对deq初始化
deque(begin,end);//将[begin,end)区间的数据拷贝给自己,不能这样初始化,可以使用insert(deque,begin,end);
* deque(num,element);//将num个element数据拷贝给自己,调用不适用,可以使用deque(deque.end(),num,element);
* deque(cons deque &deque);//拷贝构造函数,在deque不适合用(),可以使用=,即deque(自己)=deque;
deque的API (二)
#include <iostream>
#include<deque>
using namespace std;
//自定义一个遍历deque容器的方法
void printDequeInt(deque<int> &d){
deque<int>::iterator it=d.begin();
for(;it!=d.end();it++){
//*it表示取值,即*it==int(某一个值)
cout<<*it<<" ";
}
cout<<endl;//容器内所有数据遍历之后输出换行
}
}
void test1(){
/*deque的数据存取
* at(idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range
* operator[idx];//越界不报错
* front();//返回第一个数据
* back();//返回最后一个数据
*
* deque的插入操作
* insert(position,element);//在position位置上插入element元素
* insert(position,n,element);//在position位置上插入n个element元素
* inseret(position,begin,end);//在position位置上插入[begin,end)区间的元素
*
* deque的删除操作
* clear();//删除所有数据
* erase(begin,end);//删除[begin,end)区间的元素
* erase(position);//删除position位置上的元素
*/
deque<int> deq1;
deq1.push_back(100);
deq1.push_back(200);
deq1.push_front(99999);
printDequeInt(deq1);
cout<<deq1.size()<<endl;
for(unsigned int i=0;i<deq1.size();i++){
cout<<deq1.at(i)<<" ";//越界抛出异常
//cout<<deq1[i]<<" ";//越界不抛出异常
}
cout<<endl;
deq1.insert(deq1.begin()+1,3,88888888);
printDequeInt(deq1);
cout<<deq1.size()<<endl;
deq1.erase(deq1.begin());
printDequeInt(deq1);
cout<<deq1.size()<<endl;
cout<<deq1.back()<<" "<<deq1.front()<<endl;
for(unsigned int i=0;i<deq1.size()+1;i++){
cout<<deq1.at(i)<<" ";//越界抛出异常
//cout<<deq1[i]<<" ";//越界不抛出异常
}
cout<<endl;
}
int main()
{
test1();
return 0;
}
第一个输出结果:
第二个输出结果:
tips:根据结果可知,双端动态数组deq1调用at方法,如果数据访问越界,就会抛出异常,而以数组的方式则不会抛出异常(第二个输出结果)
vector容器和deque容器的应用:
案例:学校举办一场篮球比赛,有五名选手ABCDE,十个评委,每一个评委都会给这五名选手分别打分,去除最高分和最低分,取平均分为五名选手的最终得分,平均分最高的为冠军,第二名亚军,第三名季军。
设计方法:1、创建五名选手,放到vector容器
2、遍历vector容器,取出每一个选手让10名评委打分,10个评委打的分数放进deque容器
3、sort算法对deque容器的分数排序并用pop_back和pop_front方法去掉最高分和最低分
4、deque容器遍历一遍,累加分数/deque.size(),获取平均分
5、对每一个人的平均分比较,筛选出冠军、亚军、季军
案例输出:
#include <iostream>
#include<string>
#include<vector>
#include<time.h>
#include<deque>
#include<algorithm>
using namespace std;
class Person
{
friend void playGame(vector<Person> &v);
friend void showPersonScore(vector<Person> &v);
private:
string name;
double score;
public:
Person() {}
Person(string name,double score){
this->name=name;
this->score=score;
}
};
void createPersonToVector(vector<Person> &v){
string tmpName="ABCDE";
int i=0;
for(i=0;i<5;i++){
string name="选手";
name+=tmpName[i];
v.push_back(Person(name,0.0));
}
}
void playGame(vector<Person> &v){
//设置随机数种子
srand(time(NULL));
//逐个选手参加比赛
vector<Person>::iterator it=v.begin();
for(;it!=v.end();it++){
//设置deque容器,把10个评委打分放进去
deque<int> d;
int i=0;
for(;i<10;i++){
//使用rand方法产生随机数,模拟10个评委打分
d.push_back(rand()%41+60);//限制分数最低60,最高100
}
//sort方法排序并去掉最低分和最高分
sort(d.begin(),d.end());
d.pop_back();
d.pop_front();
//accumulate方法求和
int sum=accumulate(d.begin(),d.end(),0);
//求平均分
(*it).score=(double)sum/d.size();
}
}
void showPersonScore(vector<Person> &v){
sort(v.begin(),v.end(),[](const Person &a,const Person &b){
return a.score>b.score;
});
//输出排序后的结果
cout<<"冠军:"<<v[0].name<<",得分:"<<v[0].score<<endl;
cout<<"亚军:"<<v[1].name<<",得分:"<<v[1].score<<endl;
cout<<"季军:"<<v[2].name<<",得分:"<<v[2].score<<endl;
//遍历所有选手和得分结果
cout<<"所有选手和得分公示结果如下:"<<endl;
vector<Person>::iterator it=v.begin();
for(;it!=v.end();it++){
cout<<"选手:"<<(*it).name<<",得分:"<<(*it).score<<endl;
}
}
int main()
{
//将五名选手放进vectr容器
vector<Person> v;
createPersonToVector(v);
//五名选手参加比赛
playGame(v);
//遍历选手成绩
showPersonScore(v);
return 0;
}
执行第一次程序,使用随机数输出结果:
执行第二次程序,使用随机数输出结果:
tips:每一次输出的结果是随机的,这里的随机是依赖srand和rand方法,严格来说不算真正的随机,只能是模拟随机
总结:
vector容器和deque容器的区分点:
1、vector 单端动态数组 空间连续,deque 双端动态数组 空间分段连续
2、vector 有容量的概念 deque容器没有容量的概念
3、vector 一端操作数据,deque双端操作数据(常数项时间)
4、vector和deque都是随机访问迭代器
tips:合适的场景选用合适的容器,才是最佳的选择,而不是选择一个你所擅长的容器或者你所喜欢的容器