单调栈总结

力扣周赛遇到了单调栈,后来听y总讲思路清楚,但是实现起来自己不太好写,因此总结一下。

什么是单调栈

  1. 单调递增栈:单调递增栈就是从栈底到栈顶数据是从大到小
  2. 单调递减栈:单调递减栈就是从栈底到栈顶数据是从小到大

解决的问题

能够在 O ( n ) O(n) O(n)找到某个位置他左右两侧比它大或者小的第一个数的位置

分析单调递增栈:比如4,3,2,1依次入栈,就是单调递增的。

他可以找出某个位置左右两侧比它大的第一个位置。

怎么找呢?比如[4,3,2,5]序列

从左往右遍历每个元素,4 3 2入栈,接下来判断栈顶和第i个元素的大小关系

a[s.top()]<a[i]说明5入栈的话会破坏掉该栈的单调递增性质,那么就需要把栈中元素弹出,与此同时栈顶元素也找到了比它大的第一个元素的位置。继续判断也把3 4它的第一个比他们大的位置找到了。这样从左往右遍历就找到了右侧比它大的第一个位置。从右往左遍历就能找到左侧的第一个比它大的位置。

同理单调递减栈也能找到左右两侧比它小的第一个元素的位置

接下来分两种情况考虑:

  1. 求某个数它左右两侧比它小的第一个位置
  2. 求某个数它左右两侧比它大的第一个位置
//寻找a[i]左右两侧比他小的数的位置(第一个位置!!)
int a[N],l[N],r[N];
//r[i]表示在a[i]的右边比他小的第一个数的位置(第一个位置!!)
for(int i=1;i<=n;i++){
    while(s.size()&&a[s.top()]>a[i]){
        r[s.top()]=i;s.pop();
    }
    s.push(i);
}
//l[i]表示在a[i]的左边第一个比他小的数的位置(第一个位置!!)
for(int i=n;i>=1;i--){
    while(s.size()&&a[s.top()]>a[i]){
        l[s.top()]=i;s.pop();
    }
    s.push(i);
}

//寻找a[i]左右两侧比他大的数的位置(第一个位置!!)
//寻找a[i]右两侧比他大的数的位置(第一个位置!!)
for(int i=1;i<=n;i++){
    while(s.size()&&a[s.top()]<a[i]){
        r[s.top()]=i;s.pop();
    }
    s.push(i);
}
//寻找a[i]左两侧比他大的数的位置(第一个位置!!)
for(int i=n;i>=1;i--){
    while(s.size()&&a[s.top()]<a[i]){
        l[s.top()]=i;s.pop();
    }
    s.push(i);
}

最后需要知道并不是所有的 l [ i ] l[i] l[i] r [ i ] r[i] r[i]都能找出来,如果遇到区间问题就需要特判一下边界。例如

84. 柱状图中最大的矩形

思路:这个就是单调栈的应用。对于每个数我们可以找到他左右两侧比他小第一个的位置,那么矩形的面积=(r[i]-l[i]-1)*h[i]。这里就需要特判边界,我们从左往右找他第一个比他小的位置可能找不到,但是给a[n+1]=0,并且遍历到n+1,(默认认为所有元素都比0大),就能给那些找不到的元素赋值r[i]=n+1.便于后续计算。同理从右往左一样

class Solution {
public:
    int largestRectangleArea(vector<int>& h) {
        int n=h.size();
        vector<int>a(n+2),l(n+2),r(n+2);
        for(int i=1;i<=n;i++)a[i]=h[i-1];
        stack<int>s;
        for(int i=1;i<=n+1;i++){
            while(s.size()&&a[s.top()]>a[i]){
                r[s.top()]=i;s.pop();
            }
            s.push(i);
        }
        while(s.size())s.pop();
        for(int i=n+1;i>=1;i--){
            while(s.size()&&a[s.top()]>a[i]){
                l[s.top()]=i;s.pop();
            }
            s.push(i);
        }
        int res=0;
        for(int i=1;i<=n;i++){
            res=max(res,(r[i]-l[i]-1)*a[i]);
        }
        return res;
    }
};

还有一种变形。

1793. 好子数组的最大分数

实际上就是另给你一个k,要求k在这个区间。

class Solution {
public:
    int maximumScore(vector<int>& nums, int k) {
        int n=nums.size();
        vector<int>l(n+2),r(n+2),a(n+2);
        k++;
        for(int i=1;i<=n;i++)a[i]=nums[i-1];
        stack<int>st;
        for(int i=1;i<=n+1;i++){
            while(st.size()&&a[st.top()]>a[i]){
                r[st.top()]=i;st.pop();
            }
            st.push(i);
        }
        while(st.size())st.pop();
        for(int i=n;i>=0;i--){
            while(st.size()&&a[st.top()]>a[i]){
                l[st.top()]=i;st.pop();
            }
            st.push(i);
        }
        int res=0;
        for(int i=1;i<=n;i++){
            if(k>l[i]&&k<r[i])
                res=max(res,(r[i]-l[i]-1)*a[i]);
        }
        // for(int i=1;i<=n;i++){
        //     cout<<l[i]<<' ';
        // }
        // cout<<'\n';
        // for(int i=1;i<=n;i++){
        //     cout<<r[i]<<' ';
        // }
        // cout<<'\n';
        return res;
    }
};

2021-3-27更新一道

B 找山坡

原题链接

题意:

找到两个坐标,这两个坐标的值相等,并且他们之间的值都大于等于这两个坐标上的值. 这两个坐标相减最大能是多少.

思路:实际上就是找第i个数右边第一个比它小的数。不过此时要注意中间相等的数不能被放到栈里面比如[1,2,3,2,2,1]。也就是说2以后再出现跟2相等的数,我们只需要统计答案,而不需要压栈,不然会影响后面的2和栈中第一个2做差的结果。

#include <bits/stdc++.h>
#define pb push_back//vector,deque
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=1e6+5;
int a[N];
int main() {
	// solve();
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	stack<int>s;
	int res=0;
	for(int i=1;i<=n;i++){
		while(s.size()&&a[s.top()]>a[i]){
			s.pop();
		}
		if(s.size()&&a[i]==a[s.top()]){
			res=max(res,i-s.top());continue;
		}
		s.push(i);
	}
	cout<<res<<'\n';
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值