数据结构——单调栈与单调队列

单调栈

单调栈是栈内元素具有严格单调性的一种数据结构。

  模板题链接:单调栈

  由于我们要找到每个数左边第一个比它小的数,那么我们便可以发现:

  对于栈中任意一个数,如果在它右边存在一个数比它小(或相等),那么这个数便是不可能被选中的,直接弹出栈即可

  于是我们便可以进行如下操作:

  按序枚举每一个数,准备将当前这个数压栈时,从栈顶开始遍历,如果栈内当前元素比当前预进栈的数要大(或相等),则符合上述所说的性质,可以直接将其出栈,让栈顶位置减一。直到遇到一个数比当前预进栈的数小,则让栈顶位置加一,使其入栈。

  需要注意的是,为了保证栈内必然存在一个数比当前预进栈的数要小,也就是为了防止数组越界,我们可以将栈中下标为0的位置设为-1。

  代码实现:

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 1e5+10;
int n;
int a[N],top;

int find(int x)
{
    a[0]=-1;
    while(a[top]>=x)top--;
    if(top==0){a[++top]=x;return -1;}
    int now=a[top];a[++top]=x;
    return now;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        printf("%d ",find(x));
    }
    return 0;
}

  单调栈算法,每个元素至多入栈一次、出栈一次,故时间复杂度为O(n)。借助单调栈处理问题的思想在于及时排除不可能的选项,保持策略集合的高度有效性和秩序性,从而为我们作出决策提供更多的条件和可能方法。


  推荐经典习题:Largest Rectangle in a Histogram (POJ2559)

  推荐实战练题:城市游戏


单调队列

单调队列与单调栈类似,是一种队列内元素具有严格单调性的数据结构。

  模板题链接:滑动窗口

  下面以求最大值为例:

  对于队列内每一个数,如果在其右边存在一个比它大的数,则该数一定比其更优,故可将其出队

  我们从左到右枚举序列中的每一个数,然后执行以下三步操作:

  1.如果当前队首元素下标已经超出了滑动窗口的范围,则队首元素出队。

  2.若队尾元素小于预入队的数,则不断删除队尾元素,直到队尾元素的值大于该数,则将其插入队尾。

  3.若当前枚举的数的下标已经大于等于滑动窗口的大小,则可以输出,当前队首元素即是最优解。

  为了方便起见,我们在队列中存储数的下标。

  代码实现:

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1e6 + 10;

int n,k;
int a[N],q[N];

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int front=1,back=1;
    for(int i=1;i<=n;i++)
    {
        if(front<back&&q[front]<=i-k)front++;
        while(front<back&&a[q[back-1]]>=a[i])back--;
        q[back++]=i;
        if(i>=k)printf("%d ",a[q[front]]);
    }
    puts("");
    front=1,back=1;
    for(int i=1;i<=n;i++)
    {
        if(front<back&&q[front]<=i-k)front++;
        while(front<back&&a[q[back-1]]<=a[i])back--;
        q[back++]=i;
        if(i>=k)printf("%d ",a[q[front]]);
    }
    puts("");
    return 0;
}

  单调队列算法,每个元素至多入队一次、出队一次,故时间复杂度为O(n)。它的思想也是在决策集合(队列)中及时排除一定不是最优解的选择。


  推荐经典习题:最大子序和 (CH1201)


 

转载于:https://www.cnblogs.com/ninedream/p/11228646.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>