前言
单调栈是一种维护数组当前位置左右两侧比它小或大的最近的数的一种数据结构。
一、经典用法
单调栈的经典用法就是找数组当前位置的数左右两侧比它小或大的最近的数。
1.模板——单调栈结构(进阶)
#include<bits/stdc++.h>
using namespace std;
void findSmall(vector<int>&arr)
{
stack<int>index;
vector<vector<int> >ans(1000001,vector<int>(2,0));//存下标
int cur;
for(int i=0;i<arr.size();i++)
{
while(!index.empty()&&arr[i]<=arr[index.top()])//有重复,相等也弹出
{
cur=index.top();
index.pop();
ans[cur][0]=index.empty()?-1:index.top();
ans[cur][1]=i;
}
index.push(i);
}
while(!index.empty())
{
cur=index.top();
index.pop();
ans[cur][0]=index.empty()?-1:index.top();
ans[cur][1]=-1;
}
//有重复值就需要修正
//左侧必正确,修正右侧
//从右往左修正,n-1处必为-1,不用修正
for(int i=arr.size()-2;i>=0;i--)
{
if(ans[i][1]!=-1&&arr[ans[i][1]]==arr[i])//若相等
{
ans[i][1]=ans[ans[i][1]][1];//把右侧答案当答案
}
}
//输出
for(int i=0;i<arr.size();i++)
{
cout<<ans[i][0]<<" "<<ans[i][1]<<endl;
}
}
void solve()
{
int n;
cin>>n;
vector<int>arr(n,0);
for(int i=0;i<n;i++)
{
cin>>arr[i];
}
findSmall(arr);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}
一般题目给的数组中都包含重复的数,所以这里直接写有重复数时的单调栈模板。
首先得准备一个栈用来存下标。之后从左到右遍历数组,每次都往栈里压入当前位置的下标。
若要找比当前位置小的数,就让栈里保持严格大数压小数,否则小数压大数。当栈不为空,且当前位置的数违反了上述规则,就弹出栈顶。此时,刚刚弹出的数的左侧就是弹出后的栈顶,右侧就是当前位置的数。
当遍历结束后,只要栈不为空,还是重复上述结算规则,但此时右侧全为-1,因为已经遍历结束。
因为相等也弹出,所以对于重复的数,左侧肯定均正确,那么就去修正右侧,考虑从右往左修正。因为n-1处必为-1,所以从n-2处开始。若右侧有答案且答案和自己相等,就拿右侧答案的答案作为自己的答案。
这里的大压小和小压大可以理解为“山坡”和“山谷”,单调栈就是在一直维护这种趋势。
其实题目有的时候只用维护一侧,所以对于修正和相等时的处理还是要具体题目具体分析。
2.每日温度
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n=temperatures.size();
vector<int>ans(n,0);
stack<int>index;
int cur;
for(int i=0;i<n;i++)
{
while(!index.empty()&&temperatures[index.top()]<temperatures[i])
{
cur=index.top();
index.pop()