1单调栈
1.1 单调栈的定义
什么是单调栈,顾名思义,入栈时遵循单调原则。一般来说,可以求出一个元素向右或向左能扩展的最大长度,保证该区间内该元素最大或最小。
1.2 举个栗子
假设我们有一个整数数列a,元素分别为20, 10, 30,40。
对于第i个元素,我们要找到大于a[i]的第一个元素的位置。
这就适合用单调栈的性质来求解,我们可以认为a[i+k]小于a[i]那么就能继续向右扩展,元素入栈,否则不能扩展,栈内元素出栈。
下面来模拟一下单调栈(单调递减)的求解过程,我们要一直保持栈的单调性。
1:栈为空,20入栈
2:栈顶元素为20,10比20要小,10入栈
3:栈顶元素为10,30比10要大,如果加入30,那么单调性会被破坏,10出栈。此时栈顶元素为20,如果加入30,还是会破坏单调性,故20出栈,这时栈为空,30入栈。
4:栈顶元素为30,40比30要大,如果加入40,破坏单调性,30出栈,40入栈。
那么为什么一直维持栈内元素的单调性就一定是对的呢,我们先假设栈中含有元素,并且一直保持单调递减的。如果元素还没有被弹出,那么肯定是还没有遇到比他大的元素。如果元素被弹出了,那么一定是第一次遇到比他大的数了,不然元素早就被弹出了。
实现时,栈内实际上是保存的元素的索引。这样就能很快的定位。
1.3 伪代码
for(遍历数组) {
if(栈为空 || 当前元素 <= 栈顶元素) 当前元素入栈
else {
while(栈非空 && 当前元素大于 > 栈顶元素) 栈顶元素出栈、执行操作
当前元素入栈
}
} 1.4 单调栈入门例题推荐
https://www.luogu.com.cn/problem/P5788
2 校招真题
2.1 题目描述
小Q在周末的时候和他的小伙伴来到大城市逛街,一条步行街上有很多高楼,共有n座高楼排成一行。
小Q从第一栋一直走到了最后一栋,小Q从来都没有见到这么多楼,所以他想知道他在每栋楼的位置处能看到多少栋楼呢?
当前面的楼的高度大于等于后面的楼时,后面的楼将被挡住。
2.2 分析
见代码注释
2.3 代码实现
//
// Created by panda on 2020/6/19.
//
/*
* 6
* 5 3 8 3 2 5
* 3 3 5 4 4 4
* 从当前位置开始向左的一个单调递增序列长度加上向右边的一个单调递增序列个数
* 向右看时:我们从最右边开始扫描,然后维护一个单调递减的栈,这样栈中元素的个数就是我们能看到的楼房栋数
* 向左看时:我们从最左边开始扫描,同样维护一个单调递减的栈,同样栈中元素个个数就是我们能看到的楼房栋数
* 证明向右看时:如果要入栈的元素破坏了单调递减的性质,那么将要入栈的元素势必要比栈顶元素要大,等价于高楼遮住了低楼
* 这样从当前元素的左边一个看时,是不会看见破环了单调递减性质的栈顶元素,就行3 势必会破坏 2(栈顶), 5单调递减栈的性质,
* 那么从3右边的第一个8向右看时,绝对不会看到2,因为3(栈顶),2,5不会满足单调递减。
* 既然3的左边第一个都看不见,那么3的左边所有元素都会看不见
* 那么2对于3左边的元素来说就没有贡献了。
* 证明向右看时同理
*/
#include<cstdio>
using namespace std;
const int maxn = 100000 + 7;
int st[maxn]; //模拟栈,存放的时楼房的索引,这样我们就能很快的知道是第几栋楼
int left[maxn]; //left[i] 向左边看时,能看到的楼房数
int right[maxn]; //right[i] 向右看时,能看到的楼房数
int building[maxn]; //building[i] 第i栋楼的高度
int top; //模拟栈顶指针
int n; //楼房总数
int main() {
while (scanf("%d", &n) == 1) {
for (int i = 1; i <= n; i++) scanf("%d", &building[i]);
//1, 向右看
top = 0;
for (int i = n; i >= 1; i--) {
if (top == 0 || building[i] < building[st[top]]) {
right[i] = top;
st[++top] = i;
} else {
right[i] = top;
while (top != 0 && building[i] >= building[st[top]]) --top;
st[++top] = i;
}
}
//2, 向左看
top = 0;
for (int i = 1; i <= n; i++) {
if (top == 0 || building[i] < building[st[top]]) {
left[i] = top;
st[++top] = i;
} else {
left[i] = top;
while (top != 0 && building[i] >= building[st[top]]) --top;
st[++top] = i;
}
}
for (int i = 1; i <= n; i++) {
printf("%d ", left[i] + right[i] + 1);
}
printf("\n");
}
return 0;
}
版权声明:本文为giftedpanda原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。