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~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章