Lesson258 - 单调队列
【L258】單調(diào)隊(duì)列
258.1 隊(duì)列&棧
隊(duì)列:queue, FIFO
棧:stack, LIFO
258.2 單調(diào)隊(duì)列
258.2.1 題目描述
\qquad給定一個(gè)長(zhǎng)度為N的序列,以及一個(gè)整數(shù)K,要求找出該序列所有長(zhǎng)度為K的子段里面元素的最大值和最小值。
258.2.2 解題步驟
\qquad我們可以使用RMQ表來(lái)完成這道題目,時(shí)間復(fù)雜度為O(log?N)~O(\log N)~?O(logN)?。但是這道題數(shù)據(jù)范圍有一點(diǎn)大,所以不推薦使用。
\qquad現(xiàn)在要學(xué)一種新的方法:單調(diào)隊(duì)列,這個(gè)數(shù)據(jù)結(jié)構(gòu)可以用O(N)~O(N)~?O(N)?的時(shí)間復(fù)雜度完成這道題目。
\qquad單調(diào)隊(duì)列和數(shù)組一樣,只不過他的元素一定是從小到大或者從大到小按照順序來(lái)排列的。
\qquad我們可以維護(hù)一下這個(gè)單調(diào)隊(duì)列:
\qquad輸入樣例:1 3 -1 -3 5 3 6 7。
\qquadN=8, K=3
\qquad一開始的時(shí)候,單調(diào)隊(duì)列為空(NULL)。
\qquadstep 1:1進(jìn)入隊(duì)列 單調(diào)隊(duì)列:1
\qquadstep 2: 3進(jìn)入隊(duì)列,此時(shí)單調(diào)隊(duì)列的元素個(gè)數(shù)不超過查詢的數(shù)目K=3。 單調(diào)隊(duì)列:1 3
\qquadstep 3: -1進(jìn)入隊(duì)列,雖然此時(shí)單調(diào)隊(duì)列的元素個(gè)數(shù)不超過查詢的個(gè)數(shù)K=3,但是這個(gè)時(shí)候?qū)ξ苍?小于新加入的元素-1,由于在3出隊(duì)列的時(shí)候,-1還沒有出隊(duì)列,所以3就不會(huì)再有用了,因此3出隊(duì)列,同理,1也要出隊(duì)列。此時(shí)單調(diào)隊(duì)列只剩下一個(gè)元素了。單調(diào)隊(duì)列:-1
\qquadstep 4: -3進(jìn)入隊(duì)列,和step 3一樣,-3比-1晚一些入隊(duì)列,而且-3<-1,所以-1不會(huì)在有用了,-1出隊(duì)列。 單調(diào)隊(duì)列:-3
\qquadstep 5: 5入隊(duì)列 單調(diào)隊(duì)列:-3 5
\qquadstep 6: 3入隊(duì)列,和step 3一樣,3比5晚一些入隊(duì)列,而且3<5,所以5不會(huì)再游泳了,5出隊(duì)列。 單調(diào)隊(duì)列:-3 3
\qquadstep 7: 6入隊(duì)列 單調(diào)隊(duì)列:-3 3 6
\qquadstep 8: 7入隊(duì)列,但是這個(gè)時(shí)候單調(diào)隊(duì)列的長(zhǎng)度已經(jīng)超過了K=3,所以對(duì)首元素-3出隊(duì),單調(diào)隊(duì)列:3 6 7
\qquad降序和升序一樣,只不過是單調(diào)隊(duì)列要反一下
\qquad問題來(lái)了:最小(大)值是多少呢?
\qquad其實(shí)大家仔細(xì)觀察一下就可以發(fā)現(xiàn):對(duì)首元素每一次都是最小的,那是因?yàn)閱握{(diào)隊(duì)列的性質(zhì)所致的。所以查詢就是對(duì)手元素。
\qquad等于是只遍歷一遍元素就求出了區(qū)間的最小值,而且比RMQ方便許多,所以我推薦使用這種方法,時(shí)間復(fù)雜度為O(N)~O(N)~?O(N)?。
\qquad又:單調(diào)隊(duì)列可以使用Stl里面的deque來(lái)實(shí)現(xiàn)一下,deque是雙端隊(duì)列,可以訪問兩邊的元素。但是這道題數(shù)據(jù)范圍實(shí)在是太大了,不推薦使用deque求解。
\qquad總結(jié):單調(diào)隊(duì)列就是下標(biāo)單調(diào),值也單調(diào)的隊(duì)列??梢允褂胐eque來(lái)實(shí)現(xiàn),頭文件為#include \<deque>。
258.2.3 代碼
258.2.3.1 STL實(shí)現(xiàn)代碼
#include <bits/stdc++.h> using namespace std;deque <int> q;int a[1000010];int main() {// 首先來(lái)查詢區(qū)間最小值const char newline = '\n';const char space = ' ';q.clear();int n, k;cin >> n >> k;// 輸入序列for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);for(int i = 1; i <= n; i ++){while(! q.empty() && a[q.back()] >= a[i]) // 隊(duì)列{q.pop_back();}q.push_back(i);while(i - q.front() >= k) q.pop_front();char ch = space;if(i == n) ch = newline;if(i >= k) printf("%d%c", a[q.front()], ch);}q.clear();// 接下來(lái)查詢區(qū)間最大值for(int i = 1;i <= n;i ++){while(! q.empty() && a[q.back()] <= a[i]){q.pop_back();}q.push_back(i);while(i - q.front() >= k) q.pop_front();char ch = space;if(i == n) ch = newline;if(i >= k) printf("%d%c", a[q.front()], ch);}return 0; }258.2.3.2 手工實(shí)現(xiàn)代碼
#include <bits/stdc++.h> using namespace std;const int N = 1000010;int q[N], fr, ed; int val[N];bool empty() {return fr == ed; }void clear() {fr = ed = 0; }int main() {int n, k;cin >> n >> k;for(int i = 1; i <= n; i ++){scanf("%d", &val[i]);}clear();for(int i = 1; i <= n; i ++){while(! empty() && val[i] <= val[q[ed - 1]]) ed --;q[ed ++] = i;while(! empty() && i - q[fr] >= k) fr ++;if(i >= k) printf("%d ", val[q[fr]]);}printf("\n");clear();for(int i = 1; i <= n; i ++){while(! empty() && val[i] >= val[q[ed - 1]]) ed --;q[ed ++] = i;while(! empty() && i - q[fr] >= k) fr ++;if(i >= k) printf("%d ", val[q[fr]]);}return 0; }總結(jié)
以上是生活随笔為你收集整理的Lesson258 - 单调队列的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机毕业设计ssm面向智慧课堂的教学过
- 下一篇: css 去掉i标签默认斜体样式