Lesson258 - 单调队列
【L258】單調隊列
258.1 隊列&棧
隊列:queue, FIFO
棧:stack, LIFO
258.2 單調隊列
258.2.1 題目描述
\qquad給定一個長度為N的序列,以及一個整數K,要求找出該序列所有長度為K的子段里面元素的最大值和最小值。
258.2.2 解題步驟
\qquad我們可以使用RMQ表來完成這道題目,時間復雜度為O(log?N)~O(\log N)~?O(logN)?。但是這道題數據范圍有一點大,所以不推薦使用。
\qquad現在要學一種新的方法:單調隊列,這個數據結構可以用O(N)~O(N)~?O(N)?的時間復雜度完成這道題目。
\qquad單調隊列和數組一樣,只不過他的元素一定是從小到大或者從大到小按照順序來排列的。
\qquad我們可以維護一下這個單調隊列:
\qquad輸入樣例:1 3 -1 -3 5 3 6 7。
\qquadN=8, K=3
\qquad一開始的時候,單調隊列為空(NULL)。
\qquadstep 1:1進入隊列 單調隊列:1
\qquadstep 2: 3進入隊列,此時單調隊列的元素個數不超過查詢的數目K=3。 單調隊列:1 3
\qquadstep 3: -1進入隊列,雖然此時單調隊列的元素個數不超過查詢的個數K=3,但是這個時候對尾元素3小于新加入的元素-1,由于在3出隊列的時候,-1還沒有出隊列,所以3就不會再有用了,因此3出隊列,同理,1也要出隊列。此時單調隊列只剩下一個元素了。單調隊列:-1
\qquadstep 4: -3進入隊列,和step 3一樣,-3比-1晚一些入隊列,而且-3<-1,所以-1不會在有用了,-1出隊列。 單調隊列:-3
\qquadstep 5: 5入隊列 單調隊列:-3 5
\qquadstep 6: 3入隊列,和step 3一樣,3比5晚一些入隊列,而且3<5,所以5不會再游泳了,5出隊列。 單調隊列:-3 3
\qquadstep 7: 6入隊列 單調隊列:-3 3 6
\qquadstep 8: 7入隊列,但是這個時候單調隊列的長度已經超過了K=3,所以對首元素-3出隊,單調隊列:3 6 7
\qquad降序和升序一樣,只不過是單調隊列要反一下
\qquad問題來了:最小(大)值是多少呢?
\qquad其實大家仔細觀察一下就可以發現:對首元素每一次都是最小的,那是因為單調隊列的性質所致的。所以查詢就是對手元素。
\qquad等于是只遍歷一遍元素就求出了區間的最小值,而且比RMQ方便許多,所以我推薦使用這種方法,時間復雜度為O(N)~O(N)~?O(N)?。
\qquad又:單調隊列可以使用Stl里面的deque來實現一下,deque是雙端隊列,可以訪問兩邊的元素。但是這道題數據范圍實在是太大了,不推薦使用deque求解。
\qquad總結:單調隊列就是下標單調,值也單調的隊列。可以使用deque來實現,頭文件為#include \<deque>。
258.2.3 代碼
258.2.3.1 STL實現代碼
#include <bits/stdc++.h> using namespace std;deque <int> q;int a[1000010];int main() {// 首先來查詢區間最小值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]) // 隊列{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();// 接下來查詢區間最大值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 手工實現代碼
#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; }總結
以上是生活随笔為你收集整理的Lesson258 - 单调队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机毕业设计ssm面向智慧课堂的教学过
- 下一篇: css 去掉i标签默认斜体样式