单调栈结构真的很有用
单调栈结构的使用场景
单调栈解决的问题是在一个数组中每一个数,要求左边离他近的比他大的和右边离他近的比他大的数
思考的问题:如果知道所有数上得到上述要求,同时复杂度满足O(N)。
暴力解的时间复杂度是O(N^2)
单调栈结构:单调栈内,从栈底到栈顶满足从大到小。
逐个入栈出栈: 复杂度为O(N)
大的放下面,如果遇到一个数比栈顶的数大,则栈顶的数出栈,此时他的右边最近比他大的数就是让他出栈的数,而他左边最近比他大的数就是他栈内下方的那个数。
【ps】如果遇到连续相等的数,则把序号压在一起放入栈内
eg:5(0)4(1)3(2)6(3)后面括号代表所属位置
5(0)压入栈然后4(1)比5(0)小,所以将4(1)压入栈中 第三步因为3(2)比4(1)小,所以也压入栈中
6(3)比3(2)要大,此时弹出栈顶的值3(2),并且将括号内的角标改成3变成3(3),代表对于3(2)这个数而言,右边第一个比他大的数位于3的位置。
3(2)左边离他最近的比他大的值就是在3的栈中的下一个值,也就是4(1)。
比较完之后将当前的6(3)放入数组之中,之前获得了信息的值可以直接进行表示了,不需要再放入栈中。
等到最后栈中还存在内容,则此时的所有数据单独弹出,此时右边没有比他大的数,栈中下面的数值是他的左边的比他大的数值。
如果出现相等数的情况,则此时下标压在一起,弹出是也一起弹出,多次计算。
由于每个数都是进栈一次出栈一次,所以复杂度为O(N)。
题目1:构造数组的MaxTree

【思路】
1、这道题可以用大根堆来做
2、这道题可以用单调栈结构来做
【单调栈解题步骤】
遍历数组,找到并记录下每个数的左右两边距离最近的比他大的数;
若一个数没有左大数和右大数,
则他就是全局最大值,作为头结点;
如果一个数没有左大数或者没有右大数,则有唯一的父节点;
如果左右两边都有,则选择较小的大数,并挂在底下作为子节点;
这个方法不会形成森林(只有一个父节点);
不会形成多叉树,可以反证。
题目2:求最大子矩阵的大小

这里有一个直方图的引例,就是求直方图中最大矩形,就是以每一个列为高,往两边扩(比他大就可以扩展),直到扩不动或者到达边界,记录下来,乘以高就是矩形面积。 最终找出直方图的最大面积,对应代码块中的maxRecFromBottom部分。
【思路】
总体思路是将矩阵从第0行到最后一行,依次将每一行当做底,形成一个直方图,求解直方图的面积最大,也就是包含的1最多。 注意这里是把问题按层分解,然后转换成了上方的直方图引例问题
- 将第0行作为底,所有的长方形中,哪个长方形中含有的1最多。【1011】
- 此时再求以第1行作为底,所有的第0行和第1行所有的长方形中包含的1最多。【2122】
- 再将第2行的值打底,此时哪个长方形包含的1最多。【3230】
。。。
(依次类推,可以求解出矩阵中所有的矩阵包含1的数目,此时求解其中最大值就是最大的值)
复杂度:由于每次遍历一行,最后求解的也就是将整个矩阵遍历,所以最后的结果也就是O(n*m)。
//数组表示直方图最大的面积
public static int maxRecFromBottom(int[] height){
if(height == null || height.length ==0){
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack<Integer>();
//这个for循环遍历数组的每一个数
for(int i = 0;i<height.length;i++){
//当栈不为空,且当前数小于等于栈顶
while(!stack.isEmpty() && height[i] <= height[stack.peek()]){
//第一次循环j=0,k表示弹出之后底下的下标,如果没有东西则为-1.
int j = stack.pop();
int k = stack.isEmpty() ? -1:stack.peek

这篇博客介绍了单调栈在解决数组问题中的应用,包括构造数组的最大树、求最大子矩阵的大小以及数组烽火传递问题。通过单调栈结构,可以实现O(N)的时间复杂度解决问题,避免暴力解的O(N^2)复杂度。文章详细阐述了单调栈的工作原理,并给出了解决三个问题的具体思路和解题步骤。
最低0.47元/天 解锁文章
602

被折叠的 条评论
为什么被折叠?



