C++ STL 队列及其应用

一、队列的概念

这就是现实生活中的队列
这就是现实生活中的队列。

队列是一种特殊的线性表,其插入操作限定在表的一端进行,称为“入队”;其删除操作则限定在表的另一端进行,称为“出队”。插入一端称为队尾,删除一端称为队首。因此,队列也被称作“先进先出”的线性表(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,最后输出计数器就行了。

滑动窗口

(【模板】单调队列 )

分析(很麻烦):先想最小,可以用一个优先队列,小的在前面,大的在后面。把要出队的出队。
但是怎么才能知道要不要出队?有两个方法:1.查找 2.直接存下标(我用的是存下标,比较器就需要改一改了),判断当前元素下标是不是小于当前要插的是第几个元素-窗口+1,最后输出第一个就行了(因为优先队列保证第一个元素最小)最大的差不多。

赶紧写一写试试吧!

写完了,或不会写了,就往下看吧。

代码在这里↓

约瑟夫环:

#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 <queue>
using namespace std;
int a[1000000];
struct cmp1 {		//小到大
	bool operator()(const int &x,const int &y) {
		return a[x]>=a[y];
	}
};
struct cmp2 {		//大到小
	bool operator()(const int &x,const int &y) {
		return a[x]<=a[y];
	}
};
bool check(int begin,int top) {		//判断是否该出队
	return top<begin;
}
int main(int argc,char* argv[],char** env) {
	int n,k;		//k:滑动窗口大小   n:数组长度 
	priority_queue< int , vector<int> , cmp1 > qmin;		//存下标
	priority_queue< int , vector<int> , cmp2 > qmax;		//存下标
	vector<int> minn,maxn;
	cin>>n>>k;
	for(int i=1; i<=n; i++)
		cin>>a[i];
	for(int i=1; i<=n; i++) {
		//把第i个元素插入priority_queue中 
		qmin.push(i);
		qmax.push(i);
		//删元素
		while(i>k&&check(i-k+1,qmin.top())&&!qmin.empty()) {
			qmin.pop();
		}
		while(i>k&&check(i-k+1,qmax.top())&&!qmax.empty()){
			qmax.pop();
		}
		//把最小、最大加到vector中
		minn.push_back(qmin.top());
		maxn.push_back(qmax.top());
	}
	//输出
	for(int i=k-1;i<minn.size();i++)
		cout<<a[minn[i]]<<' ';
	cout<<endl;
	for(int i=k-1;i<maxn.size();i++)
		cout<<a[maxn[i]]<<' ';
	cout<<endl;
	return 0;
}

队列的应用还有很多,比如广搜(也可以说是宽搜,BFS),在这里不作讲解了。
886~

发布了7 篇原创文章 · 获赞 6 · 访问量 946

猜你喜欢

转载自blog.csdn.net/orangebench11/article/details/104581780