单调栈模板


前言

只是了解一下单调栈的解决问题的思路


一、单调栈模板

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
const ll N=1e6+10;
ll ans=0;
stack<ll>p;
signed main()
{
	IOS;
	ll t;
	cin>>t;
	while(t--)
	{
		ll x;
		cin>>x;
		while(!p.empty()&&p.top()<=x)//这是一个单调递减栈,只需要把改为>=就相当于单调递增栈
		p.pop();
		ans+=p.size();
		p.push(x);
	 } 
	 cout<<ans<<endl;
	return 0;
}

二、单调栈利用的场景

1.寻找下一个更大/小元素问题

在这里插入图片描述
例题:传送门:P2866 [USACO06NOV] Bad Hair Day S

在这里插入图片描述
解决代码

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
const ll N=1e6+10;
ll ans=0;         // 存储最终结果,记录所有元素左侧比它大的元素数量之和
stack<ll> p;      // 单调递减栈,用于维护元素左侧的较大值

signed main()
{
    IOS; 
    ll t;
    cin>>t;       // 读取元素总数
    
    // 遍历每个元素
    while(t--)
    {
        ll x;
        cin>>x;   // 读取当前元素
        
        // 步骤1:维护单调递减栈
        // 弹出所有小于等于当前元素x的栈顶元素
        // 因为这些元素不可能成为后续元素的"左侧第一个比它大的元素"
        while(!p.empty() && p.top() <= x)
            p.pop();
        
        // 步骤2:计算贡献值
        // 此时栈中的所有元素都大于当前元素x
        // 栈的大小即为当前元素左侧比它大的元素数量
        ans += p.size();
        
        // 步骤3:将当前元素压入栈中
        // 保持栈的单调性(从栈底到栈顶元素递减)
        p.push(x);
    }
    
    cout<<ans<<endl;  // 输出所有元素左侧比它大的元素数量之和
    return 0;
}

主要思路:
在这里插入图片描述

2.柱状图相关问题

在这里插入图片描述

a.最大矩形面积

题目传送门: 柱状图中最大的矩阵

在这里插入图片描述
在这里插入图片描述
解决的完整代码

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
const ll N=1e6+10;
ll a[N]; 
signed main()
{
    IOS;
    ll n;
    cin>>n; 
    stack<pii> p;    // 单调递增栈,存储{高度, 宽度}
    ll ans=0;        // 最大矩形面积
    ll wide=0;       // 累积宽度,记录弹出元素的总宽度
    
    // 读取所有柱子高度
    for(ll i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    
    // 添加哨兵元素,确保所有元素都会被弹出
    a[n+1]=0;
    
    // 遍历每个柱子(包括哨兵)
    for(ll i=1;i<=n+1;i++)
    {
        wide=0;  // 每次处理新柱子前重置累积宽度
        
        // 弹出所有比当前柱子高的栈顶元素,并计算面积
        while(!p.empty() && p.top().first > a[i])
        {
            wide += p.top().second;  // 累积弹出元素的宽度
            ans = max(ans, wide * p.top().first);  // 更新最大面积
            p.pop();  // 弹出栈顶元素
        }
        
        // 将当前柱子压入栈,宽度为累积宽度+1
        p.push({a[i], wide+1});
    }
    
    cout<<ans<<endl;  // 输出最大面积
    return 0;
}

力扣的代码

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
		stack<pair<int,int>>p;
		int ans=0;
		int wide=0;
        vector<int> a=heights;
        a.push_back(0);
		for(int i=0;i<a.size();i++)
		{
			wide=0;
			while(!p.empty()&&p.top().first>a[i])
			{
				wide+=p.top().second;
				ans=max(ans,wide*p.top().first);
				p.pop();
			}
			p.push({a[i],wide+1});
		}
		return ans;
    }
};

主要思路就是
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

b.接雨水问题

题目传送门: 接雨水
在这里插入图片描述
力扣代码:

class Solution {
public:
    int trap(vector<int>& height) {
        vector<int> a = height;                 // 复制高度数组便于操作
        int len = a.size();                     // 数组长度
        stack<int> p;                           // 单调递减栈,存储数组下标
        int ans = 0;                            // 记录总雨水量
        int top1 = 0;                           // 临时存储弹出的栈顶下标

        for (int i = 0; i < len; i++) {
            // 当当前柱子高度大于栈顶柱子高度时,形成凹槽结构
            while (!p.empty() && a[i] > a[p.top()]) {
                top1 = p.top();                 // 弹出栈顶元素,作为凹槽底部
                p.pop();
                
                // 若栈为空,说明无左边界,无法形成凹槽,跳过后续计算
                if (p.empty())
                    break;
                    
                int l = p.top();                // 新的栈顶为左边界下标
                int wide = i - l - 1;           // 凹槽宽度:右边界-左边界-1
                int hight = min(a[i], a[l]) - a[top1];  // 凹槽高度:左右边界较小值-底部高度
                
                // 累加当前凹槽的雨水量
                ans += hight * wide;
            }
            p.push(i);                          // 压入当前下标,维护单调递减栈
        }
        return ans;                             // 返回总雨水量
    }
};

对于这一问题主要求的是可以形成的凹面积,主要利用的是单调递减栈
模拟过程
在这里插入图片描述
在这里插入图片描述
具体的演示步骤可以移步到 官方视频详解


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值