大纲
1.单调栈简介&前言
2.例题
3.使用单调栈解决问题
1.单调栈简介&前言
单调栈是栈的一种特殊的形式,必须要求栈内元素单调,当题目有单调性的话(不是答案具有单调性,是信息具有单调性),一般可以使用单调栈,避免重复执行不必要的操作,从而将时间复杂度降低。
单调栈没有什么特定的算法,也没有啥模板 ,所以就不讲太多关于单调栈算法一类的东西了 就将一些例题吧。
2.例题
(1).最近的数
给定一个长度为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
思路1.暴力
暴力枚举!
每一次输入一个数,从i-1一直开始找,如果当前找到的一个数<a[i],那么记录一下下表,退出查找。
时间复杂度:
输入的for循环,O(n)
查找a[j]<a[i],并输出,查找时间复杂度:O(n)
总时间复杂度:O(n ^ 2)
但是n的范围是10 ^ 5,时间复杂度:O(10 ^ 10),计算机1ms大概能运算10 ^ 7 ~ 10 ^ 8,TTTTTLLLLLEEEEE!!!!!
思路2.单调栈
就直接讲一下怎么写吧,等一会我再讲一下为什么。
算法过程:
定义一个栈,并且记录栈顶tops
for循环,输入每一个数
不停的出栈,直到栈顶<a[i]
现在我们要确定这个算法的正确性,关键在于我们每一次出栈的元素是否会在后面的过程中作为答案输出:
现在假设按照算法的过程,判断后面一个数是否会用到前面出栈的元素。三种情况:
① 后面一个元素=当前元素,答案就是当前栈顶,没有问题。
② 后面一个元素<当前元素,出栈的元素都>=当前元素,那么那些出栈元素也一定>=后面的元素(设出栈的元素为P,当前元素为A,后面的元素为B:A>=B,P>=A则P一定>=B)
③ 后面一个元素>当前元素,若出栈的元素<后面的元素,但是他们一定>=当前元素,所以当前元素一定比出栈的元素更优。否则答案显然不可能是那些出栈的元素。
这样我们就证明了算法的正确性。
样例模拟:
第一次:栈为空,将3入栈,输出-1。 第二次:3<4,直接输出3,并且将4入栈。 第三次:将3, 4出栈,栈空了,输出-1,并且将2入栈。
第四次:2<7,直接输出2,并且将7进栈。 第五次:2同样满足,输出2
AC代码:
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, x, s[N], tops; //定义栈,并且记录栈顶
int main ()
{
cin >> n;
while (n -- )
{
cin >> x; //读入
while (tops && s[tops] >= x) tops --; //维护栈内元素单调
if (tops) cout << "-1 "; //没有一个元素<a[i]
else cout << s[tops] << ' '; //否则有解,则输出
s[++ tops] = x; //元素入栈
}
}
(2).最大子矩阵
电子屏是安装在城市的建筑物上,城市里有紧靠着的N个建筑。需要在上面找一块尽可能大的矩形放置电子屏。我们假设每个建筑物都有一个高度,从左到右给出每个建筑物的高度H1,H2…HN,且0<Hi<=1,000,000,000,并且我们假设每个建筑物的宽度均为1。要求输出广告牌的最大面积。
输入
第一行是一个数n (n <= 10 ^ 5)
第二行是n个数,分别表示每个建筑物高度H1,H2…HN,且0<Hi<=1,000,000,000。
输出
一共有一行,表示广告牌的最大面积。
样例输入