一:概念定义
单调栈可分为:单调递增栈和单调递减栈
- 单调递增栈:单调递增栈就是从栈底到栈顶数据是从大到小
- 单调递减栈:单调递减栈就是从栈底到栈顶数据是从小到大
- 但是名字其实不重要,做题目只需判断存储的数据从站栈底到栈顶的单调情况即可
二:单调栈题型
一般涉及的题目为求离某个数最近的且满足某个条件的数
三:题目描述
给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。
输入格式
第一行包含整数 N,表示数列长度。
第二行包含 N 个整数,表示整数数列。
输出格式
共一行,包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1。
数据范围
1 ≤ N ≤ 10^5
1 ≤ 数列中元素 ≤ 10^9
输入样例
5
3 4 2 7 5
输出样例
-1 3 -1 2 2
四:思路解析
朴素暴力做法:
for (int i = 0; i < n; i ++){
for (int j = i-1; j >=0; j --){
if(a[i] > a[j])
{
cout << a[j] << endl;
break;
}
}
}
显然该方法太暴力,当数据量大时就容易超时,因此我们要想方设法用到栈的特点
分析:假设我们到达了a【i】的位置,要往前寻找离a【i】最近且小于a【i】的数字,如果用上面的做法越到后面,重复检验的数字就越来越多。比如:x < y,a【x】> a【y】,显然,对应a【i】而言,找到的数字肯定不是a【x】,答案一定是在a【y】以及a【y】的后边。如下图:
因此,很多数字其实不需要一直保留在栈内部。我们这么去思考:让栈内的元素按照一定条件的筛选之后始终保持单调递增,a【i】只需要始终和栈顶元素比较,只要栈顶元素小于a【i】说明栈顶元素符合题意,如果栈顶元素大于等于a【i】,就把栈顶弹出,和下一任栈顶比较,以此类推。观察下图就容易理解:
简单粗暴的理解:我们在维护a【i+1】,我们就要利用a【i】,将a【i】之前(不包括a【i】)所有大于a【i】的数字全部弹出,让a【i】做栈顶元素
注:我们容易发现,任何时候从栈底到栈顶都是递增的,但是至于是递增栈还是递减栈不重要,我们只需要在做题目的时候判断出其栈内部的排列顺序即可。
五:万年无误代码模板
#include <iostream>
using namespace std;
const int N = 100010;
int stk[N], top;
int main()
{
int n;
cin >> n;
while (n -- )
{
int x;
scanf("%d", &x);
while (top && stk[top] >= x) top -- ;//如果栈顶元素大于当前待入栈元素,则出栈
if (!top) printf("-1 ");//如果栈空,则没有比该元素小的值。
else printf("%d ", stk[top]);
stk[ ++ top] = x;
}
return 0;
}
创作不易,建议点赞+收藏+关注,以免找不到宝贝文章了。
基础集训结束后将开展拔高系列