单调栈类型题合集

这篇博客探讨了单调栈在解决LeetCode题目中的应用,如42接雨水和84柱状图中最大的矩形。重点在于理解如何利用单调栈确定柱形面积,并介绍了解决弹栈时栈为空及遍历完成后栈中仍有元素的问题,通过设置哨兵元素来优化解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单调栈分为单调递增栈和单调递减栈。

操作规则:(下面都以单调递增栈为例)
如果新的元素比栈顶元素大,就入栈
如果新的元素较小,那就一直把栈内元素弹出来,直到栈顶比新元素小

Stack<Integer> stack = new Stack<>();
for(int i = 0; i < nums.size(); i++)
{
	while(! stack.isEmpty() && stack.peek() > nums[i])
	{
		st.pop();
	}
	st.push(nums[i]);
}

力扣42 接雨水

https://blog.csdn.net/weixin_39248750/article/details/122921911

public int trap(int[] height){
        if(height==null) return 0;
        int res=0;
        Stack<Integer> stack = new Stack<>();
        for(int i=0;i<height.length;i++){
            while(!stack.isEmpty()&&height[stack.peek()]<height[i]){
                int curIndex=stack.pop();
                while(!stack.isEmpty()&&height[stack.peek()]==height[curIndex]){
                    stack.pop();
                }
                if(!stack.isEmpty()){
                    int stackTop=stack.peek();
                    res+=(Math.min(height[stackTop],height[i])-height[curIndex])*(i-stackTop-1);
                }
            }
            stack.push(i);
        }
        return res;
    }

力扣84 柱状图中最大的矩形

思路:可以想成是求以每个矩形高度下最大的面积,于是就变成遍历整个数组,求每个下标对应的高度下最大的矩形面积。

在确定一个柱形的面积的时候,除了右边要比当前严格小,其实还蕴含了一个条件,那就是左边也要比当前高度严格小。

因此我们确定当前柱形对应的宽度的左边界的时候,往回头看的时候,一定要找到第一个严格小于我们要确定的那个柱形的高度的下标。

 

public int largestRectangleArea(int[] heights) {
        int len = heights.length;
        if (len == 0) {
            return 0;
        }
        if (len == 1) {
            return heights[0];
        }
        int res = 0;
        Deque<Integer> stack = new ArrayDeque<>(len);
        for (int i = 0; i < len; i++) {
            //单调栈思想
            while (!stack.isEmpty() && heights[i] < heights[stack.peekLast()]) {
                int curHeight = heights[stack.pollLast()];
                while (!stack.isEmpty() && heights[stack.peekLast()] == curHeight) {
                    stack.pollLast();
                }
                int curWidth;
                if (stack.isEmpty()) {
                    curWidth = i;
                } else {
                    curWidth = i - stack.peekLast() - 1;
                }
                res = Math.max(res, curHeight * curWidth);
            }
            stack.addLast(i);
        }
        //如果这时栈里还有元素,可以假设最右边还有一个下标(数组长度),高度为0
        while (!stack.isEmpty()) {
            int curHeight = heights[stack.pollLast()];
            while (!stack.isEmpty() && heights[stack.peekLast()] == curHeight) {
                stack.pollLast();
            }
            int curWidth;
            if (stack.isEmpty()) {
                curWidth = len;
            } else {
                curWidth = len - stack.peekLast() - 1;
            }
            res = Math.max(res, curHeight * curWidth);
        }
        return res;
    }

以上代码需要考虑的两个问题:

  1. 弹栈的时候,栈为空;
  2. 遍历完成以后,栈中还有元素;

解决方法:设置两个哨兵

在输入数组的左右两端设置两个哨兵,有了这两个哨兵(柱形):
左边的柱形(第 1 个柱形h=0)由于它一定比输入数组里任何一个元素小,它肯定不会出栈,因此栈一定不会为空;
右边的柱形(第 2 个柱形h=0)也正是因为它一定比输入数组里任何一个元素小,它会让所有输入数组里的元素出栈(第 1 个哨兵元素除外)。

public static int largestRectangleArea(int[] heights){
        int len=heights.length;
        if(len==0) return 0;
        if(len==1) return heights[0];
        int[] newHeights=new int[len+2];
        newHeights[0]=0;
        System.arraycopy(heights,0,newHeights,1,len);
        newHeights[len+1]=0;
        Stack<Integer> stack=new Stack<>();
        stack.push(0);
        int res=0;
        for(int i=1;i<len+2;i++){
            while(newHeights[stack.peek()]>newHeights[i]){
                int curHeight=newHeights[stack.pop()];
                while(newHeights[stack.peek()]==curHeight){
                    stack.pop();
                }
                int curWidth=i-stack.peek()-1;
                res=Math.max(res,curHeight*curWidth);
            }
            stack.push(i);
        }
        return res;
    }

力扣739 每日温度

public int[] dailyTemperatures(int[] temperatures) {
        int len=temperatures.length;
        if(len<2) return new int[0];
        int[] res=new int[len];
        Stack<Integer> stack=new Stack<>();
        for(int i=0;i<len;i++){
            while(!stack.isEmpty()&&temperatures[stack.peek()]<temperatures[i]){
                int curTem=stack.pop();
                res[curTem]=i-curTem;
            }
            stack.push(i);
        }
        return res;
    }

力扣85 最大矩形

可以看作是做了n次求柱状图中最大矩形的问题

public int maximalRectangle(char[][] matrix){
        if(matrix==null||matrix.length==0) return 0;
        int m=matrix.length;
        int n=matrix[0].length;
        //设置哨兵两个
        int[] heights=new int[n+2];
        int res=0;
        for(int i=0;i<m;i++){
            Deque<Integer> stack=new ArrayDeque<>();
            stack.addLast(0);
            for(int j=0;j<n;j++){
                heights[j+1]=matrix[i][j]=='1'?heights[j+1]+1:0;
            }
            for(int j=1;j<heights.length;j++){
                while(heights[j]<heights[stack.peekLast()]){
                    int curHeight=heights[stack.pollLast()];
                    int width=j-1-stack.peekLast();
                    res=Math.max(res,curHeight*width);
                }
                stack.addLast(j);
            }
        }
        return res;
    }

力扣581 最短无序连续子数组

public int findUnsortedSubarray(int[] nums){
        Stack<Integer> stack=new Stack<>();
        int start=nums.length;
        int end=-1;
        for(int i=0;i<nums.length;i++){
            while(!stack.isEmpty()&&nums[i]<nums[stack.peek()]){
                start=Math.min(start,stack.pop());
            }
            stack.push(i);
        }
        stack.clear();
        for(int i=nums.length-1;i>=0;i--){
            while(!stack.isEmpty()&&nums[i]>nums[stack.peek()]){
                end=Math.max(end,stack.pop());
            }
            stack.push(i);
        }
        return end-start>0?end-start+1:0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值