单调队列

单调队列

作用

  • 解决滑动窗口最值问题,使得原数组每个元素最多入队和出队1次,就能够求出所有窗口的最值,避免了重复比较求最值
  • 优化其他算法

性质

  • 以维护滑动窗口最小值为例

  • 需要构建单调递增双端队列,使得队首保存当前窗口最小值在原数列中所处的位置

  • 遍历原数组

    • 若当前值大于队尾值或者队列为空,则直接把当前值的序号插入队尾
    • 若当前值小于队尾值所指向原数组的值,则它需要挤到前面去。方法是只要当前队尾所指向的原数组的值比当前值大,就弹出队尾,直到队尾所指向的原数组的值小于当前值或者队列已经空了,再把当前值的序号插到队尾。
  • 若遍历到第i个数,窗口大小为k,只要i >= k,则每往后遍历一个数就要打印队首指向原数组的值

  • 【注意】有时候队首是不合法的,原因是队首不一定处于当前滑动窗口内部,即队首值必须处于[i-k+1 , i]之间

STL deque 使用

    #include<iostream>
    #include<deque> 
    using namespace std;
    const int maxn = 1e6+10;
    deque<int> q;
    int a[maxn];
    int main(){
        ios::sync_with_stdio(false);
        int n,k;
        while(cin>>n>>k){
            for(int i=1; i<=n; i++) cin>>a[i];
            //最小值 维护单调增队列 
            for(int i=1; i<=n; i++){
                //遇到更小的就往前挤 
                while(!q.empty() && a[q.back()] > a[i] ) q.pop_back();
                q.push_back(i);
                //删除头 保证front的位置处于滑动窗口内 即 [i-k+1 , i] 
                //这种情况只可能在 i>=k 时才需要考虑 
                if(i >= k){
                    while(!q.empty() && q.front() <= i-k) q.pop_front();
                    //打印最小值
                    cout<<a[q.front()]<<" "; 
                } 
            }
            cout<<endl;
            //q.erase(q.begin(),q.end());
            while(!q.empty()) q.pop_back();
            //最大值  维护单调减队列 
            for(int i=1; i<=n; i++){
                //遇到大的就往前挤 
                while(!q.empty() && a[q.back()] < a[i] ) q.pop_back();
                q.push_back(i);
                //删除头 保证front的位置处于滑动窗口内 即 [i-k+1 , i] 
                //这种情况只可能在 i>=k 时才需要考虑 
                if(i >= k){
                    while(!q.empty() && q.front() <= i-k) q.pop_front();
                    //打印最大值
                    cout<<a[q.front()]<<" "; 
                } 
            }
            cout<<endl;
            
        }
        
    }

数组模拟队列

    #include<iostream>
    #include<deque> 
    using namespace std;
    const int maxn = 1e6+10;
    int a[maxn];
    int b[maxn];
    int main(){
        ios::sync_with_stdio(false);
        int n,k;
        while(cin>>n>>k){
            int p=1,q=1;
            for(int i=1; i<=n; i++) cin>>a[i];
            //最小值 维护单调增队列 
            for(int i=1; i<=n; i++){
                //遇到更小的就往前挤 
                while(p < q && a[b[q-1]] > a[i] ) q--;
                b[q++] = i;
                //删除头 保证front的位置处于滑动窗口内 即 [i-k+1 , i] 
                //这种情况只可能在 i>=k 时才需要考虑 
                if(i >= k){
                    while(p < q && b[p] <= i-k) p++;
                    //打印最小值
                    cout<<a[b[p]]<<" "; 
                } 
            }
            cout<<endl;
            //q.erase(q.begin(),q.end());
            p = 1 , q = 1;
            //最大值  维护单调减队列 
            for(int i=1; i<=n; i++){
                //遇到大的就往前挤 
                while(p < q && a[b[q-1]] < a[i] ) q--;
                b[q++] = i;
                //删除头 保证front的位置处于滑动窗口内 即 [i-k+1 , i] 
                //这种情况只可能在 i>=k 时才需要考虑 
                if(i >= k){
                    while(p<q && b[p] <= i-k) p++;
                    //打印最大值
                    cout<<a[b[p]]<<" "; 
                } 
            }
            cout<<endl;
            
        }
        
    }

原文地址:https://www.cnblogs.com/czsharecode/p/10868772.html