欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

滑动窗口--单调队列!

程序员文章站 2024-02-21 14:14:51
...

单调队列

解决大小为k的区间的最值问题,区别于RMQ和线段树的是,单调队列允许移动k区间,时间复杂度O(n)

滑动窗口

给定一个大小为n≤1e6的数组。有一个大小为k的滑动窗口,它从数组的最左边移动到最右边。

您只能在窗口中看到k个数字。每次滑动窗口向右移动一个位置。

以下是一个例子:

该数组为[1 3 -1 -3 5 3 6 7],k为3。
滑动窗口--单调队列!
求出每个k大小区间的最大值和最小值。
思路:维护队列的单调性,往队尾插入元素之前,先将队尾大于等于当前数的元素全部弹出即可;
这样所有数均只进队一次,出队一次,时间复杂度是 O(n)的

代码(双端队列)

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define pb push_back
#define sc(a) scanf("%d",&a)
#define scl(a) scanf("%lld",&a)
#define scs(a) scanf("%s",a+1)
#define pf printf
const int mod=1e9+7;
const int N=1e6+10;
struct node
{
    int v,id;
}a[N];
int n,k;
deque<node>q1,q2;
int main()
{
    //3 1 -3 2 3 4 -1 5 4
    cin>>n>>k;
    for(int i=1;i<=n;i++)
        cin>>a[i].v,a[i].id=i;
    for(int i=1;i<=k;i++)
    {
        if(q1.empty())q1.push_back(a[i]);
        else {
            while(!q1.empty()&&q1.back().v>a[i].v)q1.pop_back();
            q1.push_back(a[i]);
        }
    }
    cout<<q1.front().v<<" ";
    for(int i=k+1;i<=n;i++)
    {
        while(!q1.empty()&&q1.front().id<i-k+1)q1.pop_front();
        while(!q1.empty()&&q1.back().v>a[i].v)q1.pop_back();
        q1.push_back(a[i]);
        cout<<q1.front().v<<" ";
    }
    cout<<endl;
    for(int i=1;i<=k;i++)
    {
        if(q2.empty())q2.push_back(a[i]);
        else {
            while(!q2.empty()&&q2.back().v<a[i].v)q2.pop_back();
            q2.push_back(a[i]);
        }
    }
    cout<<q2.front().v<<" ";
    for(int i=k+1;i<=n;i++)
    {
        while(!q2.empty()&&q2.front().id<i-k+1)q2.pop_front();
        while(!q2.empty()&&q2.back().v<a[i].v)q2.pop_back();
        q2.push_back(a[i]);
        cout<<q2.front().v<<" ";
    }
    return 0;
}