一、单调栈原理
单调栈里装的是什么?
动态维护一个栈,把后续问题的答案都维持在这个栈中,把肯定不再有用的数字从栈中干掉。
二、理解与感悟
1、 动态维护,随进随维护,不是预处理。
2、可以将\(O(N^2)\)的时间复杂度降为\(O(N)\)。
3、此类“左侧(或右侧)最近”,比自己大的(或小的),用单调栈。
4、为什么说明单调的呢?
下面举一个栗子:
(1) 5 4 3 2 1
栈内就是
1 2 3 4 5 单调上升的
(2) 5 4 2 1 2
2入栈 栈内:2
1比2小,2找到答案1,2出栈,1入栈 栈内:1
2比1大,2入栈 栈内:1 2
4比2大,4入栈 栈内:1 2 4
5比4大,5入栈 栈内:1 2 4 5
总结:单调上长栈
三、左边第一个比我小
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
int stk[N], tt; //栈内容:待查找答案的数字
int res[N]; //记录“左边比我小的数字”编号
/**
测试用例:左边第一个比我小
6
6 10 3 7 4 12
结果:
-1 6 -1 3 3 4
*/
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//倒序遍历,寻找左侧比自己小的数字
for (int i = n; i >= 1; i--) {
while (tt && a[stk[tt]] > a[i]) {//栈不空并且当前元素i是栈顶元素左侧并且比栈顶元素小的第一个数
res[stk[tt]] = i; //为栈顶元素记录答案i
tt--; //栈顶元素出栈
}
stk[++tt] = i; //将i入栈,准备查找它左侧比它小的数字
}
//res[i]中装的是序号,是1~n的,所以可以用res[i]>0进行判断是否找到
//a[res[i]]是原始的数据值,是res[i]位置上的值
for (int i = 1; i <= n; i++) res[i] ? cout << a[res[i]] << ' ' : cout << "-1 ";
return 0;
}
四、右侧第一个比自己大的
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
int stk[N], tt;
int res[N];
/**
测试用例:右边第一个比我大
6
6 10 3 7 4 12
结果:
10 12 7 12 12 -1
*/
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//答案是查找右侧的,所以正序遍历
for (int i = 1; i <= n; i++) {
while (tt && a[stk[tt]] < a[i]) {//栈内装的是没有找到答案的数字
res[stk[tt]] = i;
tt--;
}
stk[++tt] = i;
}
for (int i = 1; i <= n; i++) res[i] ? cout << a[res[i]] << ' ' : cout << "-1 ";
return 0;
}
五、视频讲解
https://www.ixigua.com/6925435220292436494