【定义】
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表(先进先出)。
一
【分析】
用数组实现
队尾加入元素:尾指针向后挪一位 再赋值(++tail)
弹出队头元素:首指针向后挪一位 原来位置上的值自然就弹出了(head++)
查询是否为空:当首指针小于等于尾指针时 不为空 否则为空
查询队头元素:直接输出即可
【代码】
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int a[N];
int head,tail;//定义首尾指针
string s;
int main(){
int m;
cin>>m;
head=0,tail=-1;
while(m--){
cin>>s;//输入字符串
if(s=="push"){
int x;
cin>>x;
a[++tail]=x;//队尾加入元素
}
if(s=="pop") head++;//弹出队头元素
if(s=="empty"){
if(head<=tail) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
if(s=="query") cout<<a[head]<<endl;
}
return 0;
}
【分析】
定义两个队列,一边男一边女,将每个人所代表的编号放入队列。将队首元素循环放入队尾,然后弹出,循环输出每次出列即可。
【代码】
#include <bits/stdc++.h>
using namespace std;
queue<int> q1,q2;
int a,b,n;
int main(){
cin>>a>>b;
for(int i=1;i<=a;i++) q1.push(i);
for(int i=1;i<=b;i++) q2.push(i);
cin>>n;
while(n--){
cout<<q1.front()<<" "<<q2.front()<<endl;
q1.push(q1.front());q1.pop();
q2.push(q2.front());q2.pop();
}
}
【分析】
输入a和n后,先判断n是否为1,如果为1,直接输出a即可。
否则,定义两个队列分别存放2*a+1和3*a+1的值。通过列举可得,两个队列都具有单调性。因而从2开始循环到n,每一次将两个队首元素进行比较,令tt等于较小元素后再弹出较小元素(相等的情况下两个都弹出),进行下一次比较,直到全部遍历完,输出。
【代码】
#include <bits/stdc++.h>
using namespace std;
int main(){
long long a,n;
while(cin>>a>>n){
if(n==1){
cout<<a<<endl;
continue;
}//n=1时需要特判
else{
queue<long long> qa,qb;
int ta,tb,tt;
qa.push(2*a+1);
qb.push(3*a+1);//放入队列
for(int i=2;i<=n;i++){
ta=qa.front();
tb=qb.front();
if(ta==tb){
tt=ta;
qa.pop();
qb.pop();
}
else if(ta<tb){
tt=ta;
qa.pop();
}
else{
tt=tb;
qb.pop();
}//比较
qa.push(tt*2+1);
qb.push(tt*3+1);//下一次循环
}
cout<<tt<<endl;
}
}
return 0;
}
【分析】
约瑟夫问题
【代码】
#include <bits/stdc++.h>
using namespace std;
queue<int> q;
int a[105];
int main(){
int n,m,f;
cin>>n>>m;
for(int i=1;i<=n;i++){
q.push(i);
}//将下标存入队列
for(int i=1;i<=n;i++){
f=1;//从1开始
while(f<m){
q.push(q.front());
q.pop();
f++;
}
cout<<q.front()<<" ";
q.pop();
}
return 0;
}
【分析】
广搜
【代码】
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef pair<int,int> PII;
int xx[4]={-1,0,1,0};
int yy[4]={0,1,0,-1};
int a[105][105];
int n,m;
void bfs(int x,int y){
queue<PII> q;
q.push({x,y});
while(q.size()!=0){
PII hhh;
hhh=q.front();
for(int i=0;i<4;i++){
int kx,ky;
kx=hhh.first+xx[i];
ky=hhh.second+yy[i];
if(kx<=0||kx>n||ky<=0||ky>m||a[kx][ky]==0) continue;
q.push({kx,ky});
a[kx][ky]=0;//将走过的标记为0
}
q.pop();//
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==1){
ans++;
bfs(i,j);
}
}
}
cout<<ans;
return 0;
}
【拓展】
pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair,另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量。
如果定义多个相同的pair类型对象,可以使用typedef简化声明。
直接用first和second访问
二.单调队列
【定义】
具有单调性质和队列性质的数据结构。
【特点】
时间复杂度为O(n) 。 单调队列可以保证,对于任何一个数而言,只会在队列中出现一次,一旦这个数对于最后答案没有贡献了,就会及时地将它删除。
【分析】
1.需要输出最小值和最大值,首先考虑到单调性。
2.窗口不断移动,每次需要解决出窗口的队首元素。
3.当队列中的数不满足单调性时,及时弹出并更新最大(最小)值。
4.最大和最小值分别求时,中间一定要重置head和tail。
【代码1 数组模拟】
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N];
struct ST{
int x,i;
}; //x为每一次进来的数,i用来表示下标
ST q[N];
int head,tail;
int n,k;
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
head=1,tail=0;
for(int i=1;i<=n;i++){
if(head<=tail&&i-q[head].i>=k) head++;//队列不为空且两数间长度大于等于窗口长度,不断移动
while(head<=tail&&q[tail].x>a[i]) tail--;//当进来的数比当前队尾的数小,弹出不需要的数
tail++;
q[tail].x=a[i];
q[tail].i=i;//赋值
if(i>=k) printf("%d ",q[head].x);
}//维护单调递增的队列,输出最小值
cout<<endl;
head=1,tail=0;
for(int i=1;i<=n;i++){
if(head<=tail&&i-q[head].i>=k) head++;
while(head<=tail&&q[tail].x<a[i]) tail--;//当进来的数比当前队尾的数大,弹出不需要的数
tail++;
q[tail].x=a[i];
q[tail].i=i;
if(i>=k) printf("%d ",q[head].x);
}//维护单调递减的队列,输出最大值
return 0;
}
【代码2 双端队列】
#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
const int N=1e6+10;
int a[N];
struct ST{
int x,i;
};
deque<ST> q;
int main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
if(q.size()&&i-q.front().i>=k) q.pop_front();
while(q.size()&&q.back().x>a[i]) q.pop_back();
ST t;
t.x=a[i];
t.i=i;
q.push_back(t);
if(i>=k) printf("%d ",q.front().x);
}
cout<<endl;
while(q.size()) q.pop_front();
for(int i=1;i<=n;i++){
if(q.size()&&i-q.front().i>=k) q.pop_front();
while(q.size()&&q.back().x<a[i]) q.pop_back();
ST t;
t.x=a[i];
t.i=i;
q.push_back(t);
if(i>=k) printf("%d ",q.front().x);
}
return 0;
}
【分析】
需要看过所有名家的画,首先让每一幅名画入队,同时画家数++。对于一幅名画,如果先前有和它属于同一个画家的画,那么先前的画就不需要了。这样从左往右找,直到找到最小值出现的区间。
【代码】
#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
const int N=1e6+5;
int a[N],q[2005];//a用来存画作,q用来存画家数
int ans=1e7;
int hh,tt,la,lb;
int main(){
int n,m;
cin>>n>>m;
int cnt=0;
hh=1,tt=0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(q[a[i]]==0) cnt++;//画家数++
q[a[i]]++;//画作数++
tt++;
while(q[a[hh]]>=2){//当一个画家的画作出现两次以上
q[a[hh]]--;//不需要
hh++;
}
if(cnt>=m&&tt-hh+1<ans){//当画家数等于m且有最小区间
ans=tt-hh+1;
la=hh;
lb=tt;//此时的hh和tt分别为左端点和右端点
}
}
cout<<la<<" "<<lb;
return 0;
}
【代码】
#include <iostream>
#include <cstdio>
using namespace std;
const int N=1e6+10;
struct ST{
int x,i;
}q[N];
int a[N],s[N];
int hh,tt,n,m;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
}//求前缀和
hh=1,tt=0;
int ans=0;
q[++tt].x=0;
q[tt].i=0;//赋初值便于求前缀和
for(int i=1;i<=n;i++){//维护一个单调递增的队列
if(hh<=tt&&i-q[hh].i>m) hh++;
while(hh<=tt&&q[tt].x>=s[i]) tt--;
tt++;
q[tt].x=s[i];
q[tt].i=i;//更新最大值
if(hh<=tt ){
ans=max(ans,s[i]-q[hh].x);
}
}
cout<<ans;
}
三.优先队列
【定义】
具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
【特点】
队首元素一定是优先级最高的那个,可以在任何时候往优先级队列中加入元 素,而优先级队列底层的数据结构是堆会随时调增结构,使得每次的队首元素都是优先级最大的。
【操作】
- top 访问队头元素
- empty 队列是否为空
- size 返回队列内元素个数
- push 插入元素到队尾 (并排序)
- emplace 原地构造一个元素并插入队列
- pop 弹出队头元素
- swap 交换内容
【使用】
priority_queue<typename> name;//大根堆
priority_queue<typename,vector<typename>,greater<typename> > name;//小根堆
结构体的优先级设置
struct fruit{
string name;
int price;
friend bool operator < (fruit f1, fruit f2) {
return f1.price < f2.price;//表明f2.price的优先级更高 因而价格高的在前面
}
}
【代码】
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
int a[30005];
priority_queue<int,vector<int>,greater<int> > q;
int main(){
int n,k;
cin>>n;
for(int i=1;i<=n;i++){
cin>>k;
q.push(k);
}
int a,b,ans=0;
while(q.size()>=2){
a=q.top();q.pop();
b=q.top();q.pop();
ans+=a+b;
q.push(a+b);
}
cout<<ans;
return 0;
}