栈操作之单调栈III

        在看这篇文章之前,建议先看看栈操作之单调栈II。这次继续分享一道关于单调栈的题。

柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例 1:
在这里插入图片描述
注:图片转载自leetcode官网
输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

class Solution {
    public int largestRectangleArea(int[] heights) {
        Deque<Integer> stack = new ArrayDeque<>();
        int[] height = new int[heights.length + 2];
        for(int i = 0;i < heights.length;i++){
            height[i + 1] = heights[i];
        }
        int ans = 0;
        for(int i = 0; i < height.length;i++){
            if(stack.isEmpty() || height[stack.peek()] <= height[i]){
                stack.push(i);
            }else{
                while(!stack.isEmpty() && height[stack.peek()] > height[i]){
                    int mid = stack.pop();
                    if(!stack.isEmpty()){
                        int w = i - stack.peek() - 1;
                        int h = height[mid];
                        ans = Math.max(w * h, ans);
                    }
                }
                stack.push(i);
            }
        }
        return ans;
    }
}

        以[2,1,5,6,2,3]为示例,说明上述代码执行的过程。首先定义一个栈,同时定义一个ans用于保存柱状图中柱子所能勾勒的最大矩形面积。
        这里需要在数组的首尾个扩充一个零元素,得到[0, 2, 1, 5, 6, 2, 3, 0]。为什么这样做,等会解释。首先遍历元素0,由于stack为空,因此将0元素的下标压入栈中,得到stack为[0],对应的值为[0]。之后遍历元素2,栈顶元素0小于2,将元素2对应的下标压入栈中,stack为[1, 0],对应的值为[2, 0]。之后遍历元素1,此时栈顶元素2大于当前遍历元素1,需要将栈中大于1的所有元素对应的下标出栈,进入else,进入while循环,将栈顶下标1出栈,mid = 1,对应的值为2,i为2,对应的值为1,stack.peek()为0,对应的值为0,也就是左侧柱子高度为0,中间高度为2,右侧柱子高度为1,计算三根柱子围成的面积。w = i - stack.peek() - 1 = 2 - 0 - 1 = 1。h = height[mid] = 2。面积就等于w * h = 2 * 1 = 2,如下图红色区域所示。此时stack为[0],对应的值为[0],由于0小于1,元素0不需要出栈,最后将元素1对应的下标压入栈中,stack为[2, 0],对应的值为[1, 0]。
在这里插入图片描述
        之后遍历元素5,5大于栈顶元素1,将5对应的下标压入栈中,stack为[3, 2, 0],对应的值为[5, 1, 0],之后遍历元素6,6大于栈顶下标对应的元素5,将6对应的下标压入栈中,stack为[4, 3, 2, 0],对应的值为[6, 5, 1, 0],之后遍历元素2,2小于栈顶下标对应的元素6,进入else,进入while循环,将下标4弹出,mid = 4,值为6,i为5,值为2,stack.peek()为3,值为5,也就是说左侧高度为5,中间高度6,右侧高度为2,计算三个柱子围成矩形的最大面积,w = i - stack.peek() - 1 = 5 - 3 - 1 = 1。h = height[mid] = 6,面积为w * h = 1 * 6 = 6,如下图红色区域所示。此时stack为[3, 2, 0],值为[5, 1, 0]。
在这里插入图片描述
        栈顶下标对应的元素5还是大于当前元素2,因此while循环继续,将元素5下标弹出,此时mid = 3,值为5,i = 5,值为2,stack.peek()为2,值为1。左侧柱子高度为1,中间柱子高度为5,右侧柱子高度为2。w = i - stack.peek() - 1 = 5 - 2 - 1 = 2。h = height[mid] = 5。面积为w * h = 2 * 5 = 10,如下图红色区域所示。此时stack为[2, 0],对应的值为[1, 0]。
在这里插入图片描述
        栈顶下标对应的元素为1,小于2,while循环结束,将元素2对应的下标压入栈中,stack为[5, 2, 0],对应的值为[2, 1, 0]。之后遍历元素3,3大于栈顶下标对应的元素2,将元素3对应的下标压入栈中,stack为[6, 5, 2, 0],对应的值为[3, 2, 1, 0]。遍历到最后一个元素0,需要将栈中所有大于0的元素全部弹出。进入while循环,首先将下标6弹出,mid = 6,值为3,i为7,值为0,stack.peek()为5,值为2。左侧柱子高度为2,中间柱子高度为3,右侧柱子高度为0。w = i - stack.peek() - 1 = 7 - 5 - 1 = 1。h = height[mid] = 3。面积为w * h = 3 * 1 = 3,如下图红色区域所示。此时stack为[5, 2, 0],对应的值为[2, 1, 0]。
在这里插入图片描述
        接着将下标5对应的元素出栈,mid = 5,值为2,i = 7,值为0,stack.peek()为2,值为1。w = i - stack.peek() - 1 = 7 - 2 - 1 = 4,h = height[mid] = 2。面积为w * h = 4 * 2 = 8,如下图红色区域所示。此时stack为[2, 0],对应的值为[1, 0]。
在这里插入图片描述
        接着栈顶下标2出栈,mid = 2,值为1,i = 7,值为0,stack.peek() = 0,值为0。左侧高度为0,中间高度为1,右侧高度为0。w = i - stack.peek() - 1 = 7 - 0 - 1 = 6。h = height[mid] = 1,面积为w * h = 6 * 1 = 6,如下图红色区域所示。此时stack为[0],对应的值为[0]。最后将0对应的下标7压入栈中,stack为[7, 0],对应的值为[0, 0]。
在这里插入图片描述
        可以比较明显的发现栈中存放元素对应的下标总是降序的。接下来说明为什么需要首尾加入元素0,首先为什么在数组末尾加入0,原因是如果数组是升序的如[1, 2, 3, 4, 5, 6, 7],那么进入for循环后,将一直执行第一个if,不会进入else中,最终stack为[6, 5, 4, 3, 2, 1, 0]。为什么在数组起始加入0,如果数组降序[7, 6, 5 ,4 ,3, 2, 1],stack由于空压入7对应的下标,之后遍历6,6小于7,需要将7弹出,进入else,进入while,mid = 7,但是stack为空了,所以不会进入计算矩形面积的逻辑。之后将6压入栈中。如此重复,虽然会进入else,进入while,但是一直都不会进入计算矩形面积的逻辑中,所以需要在数组起始添加0。

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单调栈是一种常见的数据结构,在解决一些特定问题时非常有用。它可以用来求解一些范围内的最大值、最小值、最长连续递增子序列等问题。下面是一个简单的 Python 实现: ``` class MonotonicStack: def __init__(self): self.stack = [] def push(self, val): while self.stack and self.stack[-1] < val: self.stack.pop() self.stack.append(val) def pop(self): self.stack.pop() def top(self): return self.stack[-1] def is_empty(self): return len(self.stack) == 0 ``` 这个单调栈实现了四个基本操作: - push(val):将元素 val 压入中。在压入之前,如果顶元素比 val 小,则弹出顶元素,直到顶元素比 val 大或为空,然后将 val 压入中。 - pop():弹出顶元素。 - top():返回顶元素。 - is_empty():判断是否为空。 使用单调栈的时候,需要根据具体问题来实现 push 操作。下面是一个例子,求解一个数组中每个元素右边第一个比它大的数: ``` def next_larger(nums): n = len(nums) res = [-1] * n stack = MonotonicStack() for i in range(n): while not stack.is_empty() and nums[stack.top()] < nums[i]: res[stack.top()] = nums[i] stack.pop() stack.push(i) return res ``` 在这个例子中,我们维护了一个单调递减的,每次遇到一个比顶元素大的数,就将顶元素弹出,并将顶元素的答案设为当前数。这样,最后中剩下的元素都没有右边比它们大的数,它们的答案就是 -1。时间复杂度为 O(n)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值