一、队列的概念
这就是现实生活中的队列。
队列是一种特殊的线性表,其插入操作限定在表的一端进行,称为“入队”;其删除操作则限定在表的另一端进行,称为“出队”。插入一端称为队尾,删除一端称为队首。因此,队列也被称作“先进先出”的线性表(FIFO,First In First Out)。
二、队列的操作
1.定义
queue<typename> 队列名字;
其中typename是想要的数据类型。
哦,对了,要使用队列,还需要加上一个头文件:(加了万能头文件就不需要了)
#include <queue>
如果要存int,可以这么写:
queue<int> q;
2.队列的函数
假设已经定义一个队列,名字是q,那么就有这些函数:
q.front(); //获取队首
q.back(); //获取队尾
q.push(x); //插入元素,x表示要插入的值,什么都行(但是类型必须和定义的相同)
q.pop(); //将队头弹出,无返回值
q.size(); //返回队列里有多少个元素
q.empty(); //如果队列为空,返回true,否则返回false( 等同于q.size()==0 )
q.swap(q2); //交换q和q2里面的值(q2需要和q是一个类型)
queue默认是空队列,如果不想怎么办?
假设已经定义了q1是queue<int>类型,那么可以这么写:
queue<int> q2(q1);
举个栗子:
#include <iostream>
#include <queue>
using namespace std;
int main(int argc,char* argv[],char** env){ //不要问我为什么main要写成这样
queue<int> q;
for(int i=1;i<=10;i++)
q.push(i);
while(!q.empty()){ //队列非空就继续循环
cout<<q.front()<<' '; //每次都输出队首
q.pop(); //输出一个就删一个
}
return 0;
}
三、其他队列
1.双向队列
双向队列不仅可以在队尾插入,在队头删除,还可以在队头插入,在队尾删除(还有很多),还支持下标访问!这比数组要强多了。(但是慢)
①.定义方法
deque<typename> 双向队列名字;
其中typename是想要的数据类型。
哦,对了,要使用双向队列,还需要加上一个头文件:(加了万能头文件就不需要了)
#include <deque>
或
#include <queue>
因为在queue这个头文件里面包含了deque
如果要存int,可以这么写:
deque<int> q;
②.函数
假设已经定义一个双向队列,名字是q,那么就有这些函数:
q.push_front(x); //在队头插入元素,x表示要插入的值,什么都行(但是类型必须和定义的相同)
q.push_back(x); //在队尾插入元素,x表示要插入的值,什么都行(但是类型必须和定义的相同)
q.pop_front(); //将队头弹出,无返回值
q.pop_back(); //将队尾弹出,无返回值
q.front(); //和queue的一样
q.back(); //和queue的一样
q.size(); //和queue的一样
q.empty(); //和queue的一样
q.swap(q2); //和queue的一样
q[i] //获取第i个元素(队头是第0个)
q.at(i); //同上(不同的地方很少)
q.clear(); //清空双向队列
q.begin(); //获取首地址,返回迭代器 (待会说怎么用)
q.end(); //获取位地址+1 注意!还要加1
还有很多......
deque默认是空队列,如果不想怎么办?
假设已经定义了a是int[]类型,那么可以这么写:
deque<int> q(a,a+想要放进去的个数);
举个栗子:
#include <iostream>
#include <deque>
using namespace std;
int main(int argc,char* argv[],char** env){
deque<int> q;
for(int i=1;i<=5;i++)
q.push_back(i);
cout<<q.size()<<endl; //5
// cout<<q.at(6)<<endl; //运行会错误
for(int i=0;i<5;i++)
cout<<q[i]<<' '; //1 2 3 4 5
return 0;
}
用迭代器遍历:
#include <iostream>
#include <deque>
using namespace std;
int main(int argc,char* argv[],char** env){
deque<int> q;
for(int i=1;i<=5;i++)
q.push_back(i);
deque<int>::iterator it; //声明迭代器
for(it=q.begin();it<q.end();it++)
cout<<*it<<' '; //1 2 3 4 5
return 0;
}
2.优先队列
优先队列保证队头最大(想最小也行)但内部非有序(你是看不着的,因为每删掉一priority_queue都会把最大(或最小)的放在最前面)
①.定义方法
priority_queue<typename> 优先队列名字;
其中typename是想要的数据类型。
哦,对了,要使用优先队列,还需要加上一个头文件:(加了万能头文件就不需要了)
#include <queue>
如果要存int,可以这么写:
priority_queue<int> q;
②.函数
假设已经定义一个优先队列,名字是q,那么就有这些函数:
q.push(x); //插入元素,x表示要插入的值,什么都行(但是类型必须和定义的相同)
q.pop(); //将队头弹出,无返回值
q.top(); //和queue的一样
q.size(); //和queue的一样
q.empty(); //和queue的一样
如果要从小到大怎么办?
首先,说一下头文件(可以不要,因为queue里包含了)
#include <vector>
#include <functional>
然后,再说一下另一种声明方法:
priority_queue<类型,容器类型,比较器> q;
其中,比较器可以是greater<类型>(小到大)或less<类型>(大到小),容器必须是用数组实现的容器,比如:vector<类型>、deque<类型>但不能是list
我个人觉得很奇怪,反正是要倒着写
还有一种比较器,是自定义的:
struct cmp{
bool operator()(const int &x,const int &y){
return x>=y; //倒着写
}
};
priority_queue<int,vector<int>,cmp> q;
举个栗子:
#include <iostream>
#include <queue>
using namespace std;
int main(int argc,char* argv[],char** env){
priority_queue< int , vector<int> ,greater<int> > q; //小到大
q.push(1);
q.push(3);
q.push(2);
q.push(4);
q.push(5);
while(!q.empty()){ //输出
cout<<q.top()<<' '; //1 2 3 4 5 但内部并非有序!只不过每pop一个,
q.pop(); //都会重新找最小的放第一个。
}
return 0;
}
四、队列的应用(代码在最后)
约瑟夫环
分析:其实可以想象成一个队列,每个人出队后再入队,(除非数到m)
要求的是出队的顺序,所以可以每出队一个,就输出一个,直到只剩一个(最后一个肯定要出队)
海港
分析:这道题千万不能存船,要存人头。可以用一个数组来记24小时内每个国籍有多少人,并维护一个队列,来记录24小时内的人分别是谁,还可以在读入的同时,弄一个计数器表示有多少不同的国籍,再弄一个循环,把24小时外的去掉,如果那个国籍的人都没了,就让计数器减1,最后输出计数器就行了。
滑动窗口
(【模板】单调队列 )
分析:可以用两个deque,一个记录最小,一个记录最大,如果前面有窗口外的或者大于等于(或小于等于)当前就删掉。
赶紧写一写试试吧!
写完了,或不会写了,就往下看吧。
代码在这里↓
约瑟夫环:
#include <iostream>
#include <queue>
using namespace std;
int main(int argc,char* argv[],char** env){
queue<int> q;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
q.push(i);
int cnt=0; //报数报到几了
while(q.size()>1){ //为什么讲过了
cnt++; //报数
if(cnt<m){ //如果不应该出队
q.push(q.front());
}else{
cnt=0; //清0
cout<<q.front()<<' '; //要出队,输出
}
q.pop();
}
cout<<q.front()<<endl; //为什么讲过了
return 0;
}
海港:
#include <string.h>
#include <stdio.h>
#include <queue>
#include <time.h>
using namespace std;
#define DAY 86400
struct passenger { //乘客结构体
int country; //国籍
int t; //时间
};
int main(int argc,char* argv[],char** env){
unsigned int mp[100001],ans=0;
memset(mp,0,sizeof(mp)); //初始化
int n;
queue<passenger> q;
scanf("%d",&n);
for(int i=0;i<n;i++){
int time,m;
scanf("%d%d",&time,&m);
for(int j=0;j<m;j++){
passenger tmp;
scanf("%d",&tmp.country);
tmp.t=time;
q.push(tmp); //读入的同时扔进队列
if(mp[tmp.country]==0) //新国籍
ans++; //计数器++
mp[tmp.country]++; //这个国籍多了一个人,+1
}
while(time-DAY>=q.front().t&&!q.empty()){ //超过24小时了
int tmp=q.front().country;
mp[tmp]--; //少一个人,-1
if(mp[tmp]==0) //如果没有这个国籍的人了
ans--; //计数器--
q.pop(); //注意!这一步不能少
}
//因为OJ都用的是文件,所以可以每读入一行就可以输出
printf("%d\n",ans); //输出结果
}
return 0;
}
滑动窗口:
#include <iostream>
#include <vector>
#include <deque>
#include <stdio.h>
using std::cin;
using std::cout;
using std::endl;
using std::deque;
using std::vector;
struct node{
int x,id;
};
int main(int argc,char* argv[],char** env){
int n,k;
scanf("%d%d",&n,&k);
deque<node> dq1,dq2;
vector<int> vmin,vmax;
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
node tmp={x,i};
while(!dq1.empty()&&dq1.back().x>=x) dq1.pop_back();
dq1.push_back(tmp);
while(!dq1.empty()&&dq1.front().id<=i-k) dq1.pop_front();
if(i>=k) vmin.push_back(dq1.front().x);
while(!dq2.empty()&&dq2.back().x<=x) dq2.pop_back();
dq2.push_back(tmp);
while(!dq2.empty()&&dq2.front().id<=i-k) dq2.pop_front();
if(i>=k) vmax.push_back(dq2.front().x);
}
for(int i=0;i<vmin.size();i++) printf("%d ",vmin[i]);
printf("\n");
for(int i=0;i<vmax.size();i++) printf("%d ",vmax[i]);
return 0;
}