单调栈解决的问题
单调栈解决的问题是在一个数组中想知道所有数中,左边离他近的比他大的和右边离他近的比他大的数
思考的问题:如果知道所有数上得到上述要求,同时复杂度满足O(N)。
单调栈结构:单调栈内,从栈底到栈顶满足从大到小。
例如: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)。
应用
构造数组的MaxTree
对于这个题,可以利用大根堆的思路解决题目,按照大根堆解决整个题目。
同时也可以采用单调栈的思路来解决这个问题。
思路:
- 每一个数都可以找到左边离的最近比他大的和右边离的最近比他大的(下面用左边和右边简称)
- 已知数组中没有重复值,如果这个节点没有左边和右边,则他就是全局最大值
- 如果一个数没有左边或者没有右边,则有唯一的父节点
- 如果左右两边都有,则选择较小的那个挂在底下作为子节点
上述流程解决了形成了每个节点都只有一个父节点和一个节点最多只能有两个子节点的问题(第4步决定了最多只能有两个子节点)
代码
求最大子矩阵的大小
思路
总体思路是将矩阵从第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.