栈操作之单调栈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
    评论
单调栈是一种常用的数据结构,用于解决一类特定的问题,其中最常见的问题是找到数组中每个元素的下一个更大或更小的元素。在Codeforces编程竞赛中,单调栈经常被用于解决一些与数组相关的问题。 下面是单调栈的一般思路: 1. 创建一个空。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与顶元素进行比较。 - 如果当前元素小于等于顶元素,则将当前元素入。 - 如果当前元素大于顶元素,则将顶元素弹出,并将当前元素入。 4. 重复步骤3,直到遍历完所有元素。 这样,最后剩下的中元素就是没有下一个更大或更小元素的元素。在使用单调栈求解具体问题时,我们可以根据需要进行一些特定的操作。 例如,如果要找到一个数组中每个元素的下一个更大的元素,可以使用单调递减。具体操作如下: 1. 创建一个空和一个空结果数组。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与顶元素进行比较。 - 如果当前元素小于等于顶元素,则将当前元素入。 - 如果当前元素大于顶元素,则将顶元素弹出,并将其在结果数组中的位置记录为当前元素的下一个更大元素的索引。 4. 将当前元素入。 5. 重复步骤3和4,直到遍历完所有元素。 6. 结果数组中没有下一个更大元素的位置,可以设置为-1。 以下是一个使用单调递减求解下一个更大元素问题的示例代码: ```cpp #include <iostream> #include <stack> #include <vector> std::vector<int> nextGreaterElement(std::vector<int>& nums) { int n = nums.size(); std::vector<int> result(n, -1); std::stack<int> stack; for (int i = 0; i < n; i++) { while (!stack.empty() && nums[i] > nums[stack.top()]) { result[stack.top()] = i; stack.pop(); } stack.push(i); } return result; } int main() { std::vector<int> nums = {1,3, 2, 4, 5, 1}; std::vector<int> result = nextGreaterElement(nums); for (int i = 0; i < result.size(); i++) { std::cout << "Next greater element for " << nums[i] << ": "; if (result[i] != -1) { std::cout << nums[result[i]]; } else { std::cout << "None"; } std::cout << std::endl; } return 0; } ``` 以上代码将输出: ``` Next greater element for 1: 3 Next greater element for 3: 4 Next greater element for 2: 4 Next greater element for 4: 5 Next greater element for 5: None Next greater element for 1: None ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值