什么是单调栈
单调栈就是单调递增或者单调递减的栈,也就是栈底到栈顶递增或递减,根据单调栈的的这种结构,可以很容易想到运用单调栈可以很容易的把O(n²)的时间复杂度优化到O(n),如果使用数组的话,相对的空间复杂度也不会太高
示例
给定一个长度为 N的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。
输入格式
第一行包含整数 N,表示数列长度。
第二行包含 N 个整数,表示整数数列。
输出格式
共一行,包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1。
数据范围
1≤N≤1e5
1≤数列中元素≤1e9输入样例:
5 3 4 2 7 5
输出样例:
-1 3 -1 2 2
思路
首先思考的是暴力做法,定义一个栈,然后每次从栈顶开始寻找比当前元素小的数,找到了就直接输出,没找到输出-1,最后把当前元素入栈。暴力的做法时间复杂度是O(n²),题目N的范围可是1e5,O(n²)的时间复杂度很明显会超时,所以我们思考有没有可以优化的地方。
我们看一下有没有什么性质,看一下这个栈里面是不是有一些元素是不是永远不会作为答案输出出来。举个栗子,比如我们求下标为8的数的左边第一个比它小的数,我们这里用数组模拟栈,定义一个栈a,下标为8的数之前的数已经全部入栈了,我们假设a3>=a5,那么a3是不是永远不会作为答案输出出来,为什么a3不会永远作为答案输出呢?因为a5在a3的前面,当我们遍历栈的时候从栈顶开始遍历,肯定第一个遍历到是a5而不是a3,又因为a3>=a5,所以a3会永远不会作为答案输出.
所以我们可以发现,如果栈里面存在这样一个关系ax>=ay且x<y的话,ax可以被删掉,所以我们最后剩下的这个序列一定是一个单调序列,这也就是单调栈。
当栈已经是单调递增,这时候来了一个x,我们要在栈里面找第一个比x小的数。我们从栈顶开始找,x等下要进栈,如果栈顶元素比x大的话,由于x要进栈,栈顶元素肯定不会被输出,所以栈顶要删除,然后依次遍历栈找到比x小的数为止。如果栈顶元素比x小的话,直接输出,然后x进栈。
我们代码用数组来模拟栈,因为这样更快而且更好操作。
动画演示
代码实现
#include <iostream>
using namespace std;
const int N=1e5+10;
int s[N];
int t;
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
//如果x比栈顶元素小,删除栈顶元素,遍历栈直到找到比x小的数为止
while(t&&s[t]>=x) t--;
if(t) cout<<s[t]<<" ";//如果没有到栈底
else cout<<"-1"<<" ";//如果到了栈底
s[++t]=x;//x进栈
}
return 0;
}