单调栈
问题描述:给定一个可能含有重复值的数组 arr,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。返回所有位置相应的信息。位置信息包括:两个数字 L 和 R,如果不存在,则值为 -1,下标从 0 开始。
示例
输入:[3,4,1,5,6,2,7]
返回值:[[-1,2],[0,2],[-1,-1],[2,5],[3,5],[2,-1],[5,-1]]
方法一
思路分析
首先介绍什么是单调栈 ,就是在栈的先进后出基础之上额外添加一个特性: 从栈顶到栈底的元素是严格递增或者递减。 对于单调递增栈,若当前进栈元素为 $e$,从栈顶开始遍历元素,把小于 $e $或者等于 $e$ 的元素弹出栈,直到遇到一个大于$ e$ 的元素或者栈为空为止,然后再把$ e$ 压入栈中。对于本题来说要想找到每一个 $i $位置左边离$ i $位置最近且值比 $arr[i] $小的位置,需要构造单调递增栈,步骤如下:
- 设置一个栈$stk$,设置二维数组$array$,其中$array[i][0]$表示左边位置离$ i$ 位置最近且值比 $arr[i] $小的位置,$array[i][1]$表示右边位置离$i$位置最近
- 从开始元素出发,从左往右,如果栈不为空,且该元素大于栈顶元素,则$array[i][0]$为栈顶元素位置,而后直接将其压入栈中;
- 如果该元素小于栈顶元素,则将栈顶元素移除,继续比较,直到找到栈顶元素小于该元素的情况,此时如果栈为空,则该元素的$array[i][0]=-1$,否则$array[i][0]$为栈顶元素位置,最后该元素压入栈中
对于本题来说要想找到每一个$ i$位置右边离$ i $位置最近且值比 $arr[i] $小的位置,需要构造单调递增栈,步骤如下:
- 设置一个栈$stk$,$array[i][1]$表示右边位置离$i$位置最近最近且值比 $arr[i] $小的位置
- 从最后的元素出发,从右往左,如果栈不为空,且该元素大于栈顶元素,则$array[i][1]$为栈顶元素位置,而后直接将其压入栈中;
- 如果该元素小于栈顶元素,则将栈顶元素移除,继续比较,直到找到栈顶元素小于该元素的情况,此时如果栈为空,则该元素的$array[i][1]=-1$,否则$array[i][1]$为栈顶元素位置,最后该元素压入栈中
图解
C++核心代码
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param nums intvector * @return intvector<vector<>> */ vector<vector<int> > foundMonotoneStack(vector<int>& nums) { // write code here int top = -1; int n=nums.size(); stack<int> stk; vector<vector<int>> array(n);//定义一个5*2的数组存放结果 for (int i = 0; i < array.size(); i++) array[i].resize(2); for ( int i = 0; i < n; i++ ) { while ( !stk.empty() && nums[stk.top()] >= nums[i] ) stk.pop();//保证栈顶元素是小于待入栈的 if ( stk.empty() ) array[i][0] = -1;//栈为空,说明该位置元素左侧没有比它小的数 else array[i][0] = stk.top();//如果栈不为空,则左侧比其值小的元素位置距离最近 stk.push(i);//该元素入栈 } while(!stk.empty()) stk.pop();//清空栈 for ( int i = n - 1; i >= 0; i-- ) { while (!stk.empty() && nums[stk.top()] >= nums[i] ) stk.pop(); if ( stk.empty() ) array[i][1] = -1;//栈为空,说明该位置元素右侧没有比它小的数 else array[i][1] = stk.top();//如果栈不为空,则右侧比其值小的元素位置距离最近 stk.push(i);//该元素入栈 } return array; } };
复杂度分析
- 时间复杂度:两次循环,每次循环的复杂度为$O(n)$,循环内部存在入栈出栈的操作,要想找到每一个 i 位置左边离 $i $位置最近且值比$ arr[i] $小的位置,如果数组是逆序的,那么操作的次数为,在寻找因此总的时间复杂度为$O(n^2)$
- 空间复杂度:借助于一个二维数组以及一个栈,空间复杂度为$O(n)$
方法二
思路分析
如果说方法一是根据题目所想到的,那么我们可以直接想到的方法其实是暴力搜索的办法,首先从开始元素出发遍历,从右往左循环找到该元素左侧第一个小于该元素的位置,从左往右找到第一个小于该元素的位置,如果没有则记为-1。
C++核心代码
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param nums intvector * @return intvector<vector<>> */ vector<vector<int> > foundMonotoneStack(vector<int>& nums) { // write code here int n=nums.size(); vector<vector<int>> array(n);//定义一个5*2的数组存放结果 for (int i = 0; i < array.size(); i++) array[i].resize(2); for(int i=0;i<n;i++) { array[i][0]=-1; array[i][1]=-1; for(int j=0;j<i;j++) { if(nums[j]<nums[i])//左侧离 i 位置最近且值比 arr[i] 小的位置 array[i][0]=j; } for(int j=n-1;j>i;j--) { if(nums[j]<nums[i])//右侧离 i 位置最近且值比 arr[i] 小的位置 array[i][1]=j; } } return array; } };
复杂度分析
- 时间复杂度:两层循环,外层循环为$n$,内层循环最大为$n$,因此时间复杂度为$O(n^2)$
- 空间复杂度: 设置了一个的辅助数组,因此空间复杂度为$O(n)$