【模板】非常见数据结构【单调栈】【单调队列】

单调栈

单调栈:使用栈来维护单调序列,是一种不常见的数据结构。其应用也不广泛,但对解决特定问题如:Next Greater Num(序列中下一个大于当前元素的值),有奇效。
给你一个数组,返回一个等长的数组,对应索引存储着下一个更大元素,如果没有更大的元素,就存 -1。
例如:
给你一个数组 [59,45,51,43,31,50,56,76],你返回数组 [7,2,6,5,5,6,7,-1]。

解释:第一个59后面比59大的数是 76,索引为 7; 45后面比45大的数是 51,索引为 2;51后面比51大的数是 56,索引为 6…76后面没有比76大的数,索引为 -1。

这道题的暴力解法就是对每个元素后面都进行扫描,找到第一个更大的元素就行了。但是暴力解法的时间复杂度是 O(n^2)。

单调栈的思想是,从后往前遍历序列并加入栈中,如果当前元素大于栈顶元素说明当前元素会对栈顶元素进行覆盖(挡住后面的元素)。此时需要对栈中元素进行出栈操作直到栈空,或者遇到一个挡不住的元素(当前元素小于栈顶元素)。如图:当51入栈时,43、31和50会出栈,此时56就是对应的50的下一个最大元素,当栈中存储索引时就是下一个最大元素索引。
在这里插入图片描述
时间复杂度为o(n)
测试输入:

8
59 45 51 43 31 50 56 76 

测试输出:

7 2 6 5 5 6 7 -1 

代码:

#include<iostream>
#include<stack>
#include<vector>
using namespace std;
//next greater num

int main()
{
    int n,top=0;
    cin>>n;
    vector<int>arr(n), ans(n),stk(n+1);
    for(int i=0;i<n;i++){
        cin>>arr[i];
    }
    for(int i=n-1;i>=0;i--){
        while(top&&arr[i]>=arr[stk[top]]){
            top--;
        }
        ans[i] = top?stk[top]:-1;
        stk[++top]=i;
    }
    for(auto i:ans){
        cout<<i<<' ';
    }
    return 0;
}

单调队列

单调栈:使用队列来维护单调序列,用于解决滑动窗口最值问题以及DP优化。
例如:力扣239.滑动窗口最大值
输入:

nums = [1,3,-1,-3,5,3,6,7], k = 3

输出:

[3,3,5,5,6,7]

输出解释:

滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

注意:为了维护队列的单调性,队尾是可以进出元素的,相当于双端队列。此题维护单调递减的序列,相对于朴素算法,其共用了划窗中的公共数据。

代码:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n=nums.size(),l=0,r=-1;
        vector<int>q(n),ans;
        for(int i=0;i<n;i++){
            while(r>=l&&nums[q[r]]<nums[i]) r--;//从队尾出队维护非严格单调递减序列
            q[++r]=i;
            if(i-k>=q[l]) l++;//队首元素超过窗口范围,从队头出队
            if(i>=k-1) ans.push_back(nums[q[l]]);
        }
        return ans;
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sophon、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值