---
# System prepended metadata

title: 單調隊列
tags: [自主學習, 演算法]

---

# 單調隊列

## 概念
我們直接帶題目
[題目](https://zerojudge.tw/ShowProblem?problemid=a146)
我們像題目一樣想像一個框框
像右一次就pop一個push一個
單調對列也就是用deque儲存這個框框內有可能有利用價值的數
當deque裡的值超出框框就pop掉
有更有利用價值的出現就把前面比較沒有利用價值的的pop掉
這題的利用價值就是數的大或小

假設有兩數a,b，且a比b更有利用價值
如果a比b更右邊出現，那麼當a出現在框框後b就會變得無用
如果b比a更右邊出現，那麼兩數就都還有利用價值


所以越有價值的會在越靠近deque的front的地方(有價值的會從back開始向前一個一個比較並pop，直到遇到更有價值的)

以範測為範例講解(只模擬最大值的)
1 3 -1 -3 5 3 6 7
```
∣
1 3 -1 -3 5 3 6 7
push 1進來 deque[1]

  ∣
1 3 -1 -3 5 3 6 7
push 3進來 deque[1,3] 3比1大且3是新進的一定比1後面，pop 1 deque[3]

∣    ∣
1 3 -1 -3 5 3 6 7
push -1進來 deque[3,-1] -1比較小但在後面所以留著
ans 3(最前面的)

  ∣     ∣
1 3 -1 -3 5 3 6 7
push -3 pop 1(本來就不在了) deque[3,-1,-3]
ans 3 3
    ∣      ∣
1 3 -1 -3 5 3 6 7
push 5 pop 3 deque[-1,-3,5] 5>3 pop -3, 5>-1 pop -1 deque[5]
ans 3 3 5

       ∣     ∣
1 3 -1 -3 5 3 6 7
push 3 pop -1(本來就不在) deque[5,3]
ans 3 3 5 5

          ∣    ∣
1 3 -1 -3 5 3 6 7
push 6 pop -3(本來就不在) deque[5,3,6] -> deque[5,6]
ans 3 3 5 5 6

            ∣    ∣
1 3 -1 -3 5 3 6 7
push 7 pop 5(本來就不在) deque[6,7]
ans 3 3 5 5 6 7
```
時間複雜度$O(n)$

以剛剛那題為實作範例
說明在code裡
```cpp=
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, k;
int a[1000005];
signed main(){ios::sync_with_stdio(0);cout.tie(0);
    while(cin >> n >> k){
        if(k > n)k = n;
        memset(a,0,sizeof(a));
        for(int i = 0; i < n; i++){
            cin >> a[i];
        }
        deque<int> qmax;//存數字在陣列的位置
        deque<int> qmin;//存數字在陣列的位置
        qmax.push_back(0);
        qmin.push_back(0);
        for(int i = 1; i < n; i++){
            //要小心deque是不是空的避免NA
            while(!qmin.empty() && qmin.front() <= i - k){qmin.pop_front();}
            //qmin.front() <= i - k 是判斷有沒有超出框框
            while(!qmin.empty() && a[i] <= a[qmin.back()]){qmin.pop_back();}
            //a[i] <= a[qmin.back()] 是判斷deque內的數有是否還有價值
            qmin.push_back(i);
            if(i >= k - 1){
                cout << a[qmin.front()];
                //當前框框最有價值的就是在front
                if(i < n - 1)cout << ' ';
            }
        }cout << '\n';
        for(int i = 1; i < n; i++){
            //max的和min是一樣的
            while(!qmax.empty() && qmax.front() <= i - k){qmax.pop_front();}
            while(!qmax.empty() && a[i] >= a[qmax.back()]){qmax.pop_back();}
            qmax.push_back(i);
            if(i >= k - 1){
                cout << a[qmax.front()];
                if(i < n - 1)cout << ' ';
            }
        }cout << '\n';
    }
    return 0;
}
```
