隊列,就像他的名字同樣,排隊的人從後面接上,前面的人辦完事情就離開。前端
隊列是一種特殊的線性表,特殊之處在於它只容許在表的前端(front)進行刪除操做,而在表的後端(rear)進行插入操做,和棧同樣,隊列是一種操做受限制的線性表。進行插入操做的端稱爲隊尾,進行刪除操做的端稱爲隊頭。隊列中沒有元素時,稱爲空隊列。node
隊列的數據元素又稱爲隊列元素。在隊列中插入一個隊列元素稱爲入隊,從隊列中刪除一個隊列元素稱爲出隊。由於隊列只容許在一端插入,在另外一端刪除,因此只有最先進入隊列的元素才能最早從隊列中刪除,故隊列又稱爲先進先出(FIFO-first in first out)線性表。c++
而隊列一般也分爲兩種類型:後端
創建這種隊列必須爲其靜態分配或者動態申請一段空間,並用隊頭指針(front)和隊尾指針(rear)進行管理,當插入一個數時,隊頭指針加1,彈出一個數時,隊尾指針加1,當隊頭指針等於隊尾指針,則是一個空隊列,當隊頭指針超出分配的空間以外時,就沒法插入。這種隊列的缺點就是用過一次的空間不會再次運用,大大浪費空間。數據結構
因此爲了讓空間重複利用,當咱們對這個隊列進行插入或者刪除的操做時,只要超出了咱們分配的空間,就讓指針指向這片空間的起始位置,用取餘操做實現,可是注意空間要開的足夠大,不然會出現隊頭指針追上隊尾指針的狀況。優化
size() | 獲取隊列長度 |
push() | 插入一個數,進行入隊操做(隊頭) |
pop() | 刪除一個數,進行出對操做(隊尾) |
front() | 獲取隊頭元素 |
empty() | 判斷隊列是否爲空,是就返回1 |
不說了,我愛STLspa
咱們常常在BFS中用到隊列(你們都知道吧)而後........咱們就上一篇最蒟蒻的約瑟夫問題讓你們康康吧(話說這真是個萬能的題呢)指針
首先,咱們來看看用STL中的queue的方法作的吧code
1 #include<bits/stdc++.h> 2 using namespace std; 3 queue<int>q; 4 int n,out,now=1; 5 int main() 6 { 7 scanf("%d%d",&n,&out); 8 for (int i=1;i<=n;i++) 9 q.push(i);//初始化 10 while (!q.empty())//隊列不爲空 11 { 12 if(now==out) 13 { 14 cout<<q.front()<<" ";//打印編號 15 q.pop();//出局 16 now=1;//初始化 17 } 18 else 19 { 20 now++; 21 q.push(q.front());//排到隊尾 22 q.pop(); 23 } 24 } 25 cout<<endl; 26 return 0; 27 }
至於手寫的.....我有點懶,之後補充吧blog
普通的隊列是一種先進先出的數據結構,元素在隊列尾追加,而從隊列頭刪除。在優先隊列中,元素被賦予優先級。當訪問元素時,具備最高優先級的元素最早刪除。優先隊列具備最高級先出 (first in, largest out)的行爲特徵。一般採用堆數據結構來實現。(不要管堆是什麼玩意兒,安心用你的STL就好了)
定義優先隊列時,默認從大到小的順序出隊,咱們也能夠用重載運算符自定義優先級
priority_queue<int> q; //經過操做,按照元素從大到小的順序出隊
priority_queue<int,vector<int>, greater<int> > q; //經過操做,按照元素從小到大的順序出隊
而後寫一段程序看看腫麼樣
1 #include<bits/stdc++.h> 2 using namespace std; 3 priority_queue<int,vector<int>, greater<int> > q; 4 int main() 5 { 6 q.push(10),q.push(5),q.push(15),q.push(2),q.push(7); 7 for(int i=1;i<=5;i++) 8 { 9 cout<<q.top()<<endl; 10 q.pop(); 11 } 12 return 0; 13 }
2 5 7 10 15
size() | 獲取隊列長度 |
empty() | 判斷隊列是否爲空 |
push() | 在隊列末尾插入一個數 |
pop() | 刪除隊首元素 |
top() | 獲取隊首元素 |
最多見的就是用在拓撲排序和優化最短路中的dijkstra.........因此,排隊,又是一道例題。
Description 今天,學校老師讓同窗們排成一隊,準備帶你們出去玩,一共有 n 名同窗, 排隊的時候同窗們向老師提了 m 條要求, 每一條要求是說同窗 x 必定要排在同窗 y 以前, 老師如今想找到一種排隊方式能夠知足全部的要求,你能幫幫他嗎? Input 第一行兩個整數 n,m(1≤n≤10410^4104,1≤m≤10510^5105),表示同窗數和要求數; 如下 m 行,每行兩個整數 x,y,之間用一個空格隔開, 表示某個要求是第 x 號同窗要排在第 y 號同窗以前,全部同窗的編號由 1 開始; 輸入保證有解。 Output 輸出一行,包括 n 個整數,表示從前日後同窗的編號,用空格隔開,若是答案不惟一,輸出字典序最小的答案。 Sample Input 1 2 1 1 2 Sample Output 1 1 2 Hint 提示:使用優先隊列進行拓撲排序
1 #include<bits/stdc++.h> 2 using namespace std; 3 priority_queue<int,vector<int>,greater<int> >q;//字典序小的在前面 4 vector<int> lt[10010]; 5 vector<int> ans; 6 int in[10010]; 7 int n,m; 8 int main() 9 { 10 cin>>n>>m; 11 while(m--) 12 { 13 int a,b; 14 cin>>a>>b; 15 in[b]++;//入度(限制數) 16 lt[a].push_back(b);//存圖 17 } 18 for(int i=1;i<=n;i++) 19 { 20 if(in[i]==0)//沒有限制就隨便排 21 q.push(i); 22 } 23 while(!q.empty()) 24 { 25 int sum=q.top(); 26 q.pop(); 27 ans.push_back(sum); 28 for(int j=0;j<lt[sum].size();j++) 29 { 30 in[lt[sum][j]]--; 31 if(in[lt[sum][j]]==0) 32 q.push(lt[sum][j]); 33 } 34 } 35 for(int i=0;i<ans.size();i++) 36 cout<<ans[i]<<" "; 37 return 0; 38 }
單調隊列,即單調遞減或單調遞增的隊列。使用頻率不高,但在有些程序中會有非同尋常的做用。
他有兩個特色
1,隊列中的元素其對應在原來的列表中的順序必須是單調遞增的。
2,隊列中元素的大小必須是單調遞(增/減/甚至是自定義也能夠)
他的玄學在於能夠隊首出隊,也能夠隊尾出隊(????)
而後通常用於求區間最值問題(RMQ)
front() | 返回隊首元素 |
back() | 返回隊尾元素 |
pop_back() | 刪除隊尾元素 |
pop_front() | 刪除隊首元素 |
push_back() | 插入隊尾元素 |
push_front() | 插入隊首元素 |
咱們通常採用雙端隊列(deque)(STL大法好!!!,固然是方便啦)固然,你也能夠手寫咱們這裏全程拿滑動窗口做爲例題吧。
爲何它能夠用來求RMQ呢?別急,那樣例作解釋:
咱們拿求這一段的最小值做爲說明
咱們用q表示隊列,p表示對應下標
首先你有這樣一串數,而後輸出連續每三個中的最小值
由於隊列沒有元素,因此第一個果斷進去
q={1} p={1}
而後咱們看向3,設想若是後面兩個數都比它大,那它就還有機會啊是否是(不像我已經沒有機會了)
q={1,3} p={1,2}
而後就是-1,由於它比3小,只要-1進了隊,在後面中,有3的每個區間確定有-1,因此3徹底就是多餘的,不必再留着了,1同理
q={-1} p={3}
-3同理
q={-3} p={4}
5來了,由於大於-3,而且還有機會因此能夠加進去
q={-3,5} p={4,5}
3來了,由於小於5,因此5沒機會了,可是-3還有機會
q={-3,3} p={4,6}
6,由於6>3,因此進去,可是-3已經沒在窗口內了,因此踢出
q={3,6} p={6,7}
7來了,同理
q={3,6,7} p={6,7,8}
由於隊列是單調遞(增/減/自定義),因此輸出隊首就好了(確定是最優的)
這一段我用的STL
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,k,x; 4 struct node{//編號和值 5 int num; 6 int val; 7 }; 8 deque<node> q_max;//求最大值 9 deque<node> q_min;//求最小值 10 int ans[2][1000005];//每一段的答案 11 int cnt; 12 int main() 13 { 14 scanf("%d%d",&n,&k); 15 for(int i=1;i<=n;i++) 16 { 17 scanf("%d",&x); 18 node head; 19 head.num=i; 20 head.val=x; 21 //最大 22 while(!q_max.empty()&&x>=q_max.back().val)//不爲空而且比隊尾大,證實隊尾的那些之後沒機會最大了了 23 q_max.pop_back(); 24 q_max.push_back(head);//插入 25 while(i-k>=q_max.front().num)//判斷隊首是否在窗內,不是就刪掉 26 q_max.pop_front(); 27 //最小 ,同上 28 while(!q_min.empty()&&x<=q_min.back().val) 29 q_min.pop_back(); 30 q_min.push_back(head); 31 while(i-k>=q_min.front().num) 32 q_min.pop_front(); 33 34 if(i>=k) 35 { 36 cnt++;//記錄,隊首確定是最優的 37 ans[0][cnt]=q_max.front().val; 38 ans[1][cnt]=q_min.front().val; 39 } 40 } 41 //輸出 42 for(int i=1;i<cnt;i++) 43 cout<<ans[1][i]<<" "; 44 cout<<ans[1][cnt]<<endl; 45 for(int i=1;i<cnt;i++) 46 cout<<ans[0][i]<<" "; 47 cout<<ans[0][cnt]<<endl; 48 }
下面是手寫的(怎麼感受簡單一些)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,k; 4 int a[1000005]; 5 int q[1000005]; 6 void deque_max() 7 { 8 int h=1,t=0;//頭指針,尾指針 9 for(int i=1;i<=n;i++) 10 { 11 while(h<=t&&q[h]+k<=i)h++;//超出窗口就彈出 12 while(h<=t&&a[i]<=a[q[t]])t--;//比隊尾大,隊尾的沒機會了也彈出 13 q[++t]=i;//入隊 14 if(i>=k)printf("%d ",a[q[h]]);//輸出 15 } 16 cout<<endl; 17 } 18 void deque_min()//同上 19 { 20 int h=1,t=0; 21 for(int i=1;i<=n;i++) 22 { 23 while(h<=t&&q[h]+k<=i)h++; 24 while(h<=t&&a[i]>=a[q[t]])t--; 25 q[++t]=i; 26 if(i>=k)printf("%d ",a[q[h]]); 27 } 28 cout<<endl; 29 } 30 int main() 31 { 32 scanf("%d%d",&n,&k); 33 for(int i=1;i<=n;i++) 34 scanf("%d",&a[i]); 35 deque_max(); 36 memset(q,0,sizeof(q));//清空隊列 37 deque_min(); 38 }
size() | 獲取棧的深度 |
top() | 獲取棧頂元素 |
pop() | 彈出棧頂元素 |
push() | 向棧頂插入元素 |
empty() | 判斷棧是否爲空 |
單調遞增或單調減的棧,跟單調隊列差很少,可是隻用到它的一端,利用它能夠用來解決一些ACM/ICPC和OI的題目,如RQNOJ 的諾諾的隊列等。
由於單調棧仍是隻用它的一段,因此和普通的棧沒什麼明顯的區別。一樣的,能夠手寫也能夠用STL自帶的
直接單調遞減的的棧,仍是拿樣例過一遍(本身編的不要在乎)
2進來
s={2} p={1}
6進來,由於6>2,因此2不可能成爲後面的數左邊第一個比本身的大的數,因此能夠踢掉,而後2的右邊最大的就是6
s={6} p={2}
8進來,同理,而後6右邊第一個比本身大的就是8
s={8} p={3}
1進來,萬一遇到比一小的還有但願,而後8就是1右邊最大的
s={8,1} p={3,4}
5進來,同理,1出去,5是1右邊大的,8是5右邊大的
s={8,5} p={3,5}
而後康康代碼
1 #include<bits/stdc++.h> 2 #define MAX 1000005 3 using namespace std; 4 int n,top,ans; 5 int h[MAX],v[MAX],sum[MAX]; 6 int s[MAX]; 7 int main() 8 { 9 scanf("%d",&n); 10 for(int i=1;i<=n;i++) 11 { 12 scanf("%lld%d",&h[i],&v[i]);//高度 和能量 13 while(top&&h[s[top]]<h[i])sum[i]+=v[s[top--]];//遇到小的就退出,而後小的右邊第一個大的就是它,加上去 14 sum[s[top]]+=v[i];//而後棧頂比本身大,確定是左邊第一個比本身大的 15 s[++top]=i;//進棧 16 } 17 for(int i=1;i<=n;i++) 18 ans=max(ans,sum[i]);//循環過一遍 19 cout<<ans; 20 } 21
好啦,但願這篇博客對大家理解棧和隊列有更好的幫助!