单调栈学习小思考

单调栈

单调栈简介

单调栈本质就是栈,其元素保持单调。单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)

  • 从栈底到栈顶,依次递减的是单调递减栈。
  • 从栈底到栈顶,依次递增的是单调递增栈。

构造单调栈,可以用于解决数组中元素右侧下一个更大(更小)元素,或者左侧下一个更大(更小)元素的一类问题。例如Leetcode中的Q84,Q85,Q496,Q503,Q739等。

单调栈的构造

如果是下一个元素,即是求一个元素的右侧满足第一个条件的是倒序遍历数组
是左侧的话就是顺序遍历数组。
如果是找更大的元素就是单调递减栈
更小的元素是单调递增栈。

代码模板如下:

求解数组中右侧下一个更小元素的代码

public int[] increasing(int[] nums){
    Deque<Integer> stack=new LinkedList<>();
    for (int i = nums.length-1; i >=0; i--) {
        //小于栈顶元素,弹出
        while (!stack.isEmpty()&&nums[i]<stack.peek())
        {
            stack.pop();
        }
        //中间根据实际问题插入具体的操作
        //大于栈顶就将此时的数组元素入栈
        stack.push(nums[i]);
    }
     return nums;
}

求解数组中右侧下一个更大元素的代码

public int[] decreasing(int[] nums){
    Deque<Integer> stack=new LinkedList<>();
    for (int i = nums.length-1; i >=0 ; i--) {
        while (!stack.isEmpty()&&nums[i]>stack.peek())
        {
            stack.pop();
        }
        //中间根据实际问题插入具体的操作
        stack.push(nums[i]);
    }
    return nums;
}

求解数组中左侧下一个更小元素的代码

public int[] increasing(int[] nums){
    Deque<Integer> stack=new LinkedList<>();
    for (int i =0; i <=nums.length-1; i++) {
        //小于栈顶元素,弹出
        while (!stack.isEmpty()&&nums[i]<stack.peek())
        {
            stack.pop();
        }
        //中间根据实际问题插入具体的操作
        //大于栈顶就将此时的数组元素入栈
        stack.push(nums[i]);
    }
     return nums;
}

求解数组中左侧下一个更大元素的代码

public int[] increasing(int[] nums){
    Deque<Integer> stack=new LinkedList<>();
    for (int i = 0; i <=nums.length-1; i++) {
        //小于栈顶元素,弹出
        while (!stack.isEmpty()&&nums[i]>stack.peek())
        {
            stack.pop();
        }
        //中间根据实际问题插入具体的操作
        //大于栈顶就将此时的数组元素入栈
        stack.push(nums[i]);
    }
     return nums;
}

典型例子

1.84. 柱状图中最大的矩形 - 力扣(LeetCode) (leetcode-cn.com)

求柱状图中的最大矩形。一个容易想到的算法就是,从一个矩形向左/向右看,如果值大于自己,则这个矩形可以被包含,可以延展过来,找到第一个小于自己的矩形就是向两侧延展的最多的长度。暴力求解O(n^2)的时间复杂度,无法通过,超时。使用单调栈求出两个数组来存储原来数组中左侧和右侧第一个小于自己元素的位置便可以在O(n)时间复杂度下完成求解。

package JavaCode.leetcode.DataStructure.StackandQueue.Monotonous_stack;

import java.util.*;
import java.util.zip.InflaterInputStream;

/*
求解矩形的最大面积
可以通过构造倒序下一个最小值
逆序,单调递增栈
 */
public class Q84 {

    public  static   int largestRectangleArea(int[] heights) {
        //求出所给数组右侧第一个更小元素的位置
        Deque<Integer> stack1=new LinkedList<>();
        int[] left=new int[heights.length];
        for (int i = 0; i <= heights.length-1 ; i++) {
            while (!stack1.isEmpty()&&heights[i]<=heights[stack1.peek()]){
                stack1.pop();
            }
            //存在更小值,就将其位置取出,不存在取-1
            left[i]=(stack1.isEmpty())?-1:stack1.peek();
            stack1.push(i);
        }
        //求出所给数组左侧第一个更小元素的位置
        Deque<Integer> stack2=new LinkedList<>();
        int[] right=new int[heights.length];
        for (int i = heights.length-1; i >=0 ; i--) {
            while (!stack2.isEmpty()&&heights[i]<=heights[stack2.peek()]){
                stack2.pop();
            }
            //存在更小值,就将其位置取出,不存在取-1
            right[i]=stack2.isEmpty()?-1:stack2.peek();
            stack2.push(i);
        }
       //遍历每个矩形,求出其能够构造的矩形往左右延申的长度,进而求出矩形面积,找到最大矩形。
        int max=0,len= heights.length;
        for (int i = 0; i < len; i++) {
            int leftIndex=(left[i]==-1)?0:left[i]+1;
            int rightIndex=(right[i]==-1)?len-1:right[i]-1;
            int area=heights[i]*(rightIndex-leftIndex+1);
            max=Math.max(max,area);
        }
        return max;

    }
    //求出给定元素左侧第一个小于自己的元素的位置


}
posted @ 2021-09-22 16:00  汪小川  阅读(171)  评论(0编辑  收藏  举报