• 单调栈-84. 柱状图中最大的矩形


    单调栈-84. 柱状图中最大的矩形

    1.题目

    本题与42. 接雨水基本一致,建议两个结合着做

    给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

    求在该柱状图中,能够勾勒出来的矩形的最大面积。

    示例 1:

    在这里插入图片描述

    输入:heights = [2,1,5,6,2,3]
    输出:10
    解释:最大的矩形为图中红色区域,面积为 10
    
    
    • 1
    • 2
    • 3
    • 4

    示例 2:

    在这里插入图片描述

    输入: heights = [2,4]
    输出: 4
    
    来源:力扣(LeetCode)
    链接:https://leetcode.cn/problems/largest-rectangle-in-histogram
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.解题思路

    解法一:动态规划

    通过题目理解,是要求最大的两个柱子(也可能是单个柱子)的最大面积,可以考虑用动态规划来做

    和42.接雨水那题有些类似,不同点在于,该题需要寻找当前柱子左边和右边小于其的柱子的索引,因此需要记录的是索引

    请添加图片描述

    方法二:单调栈

    本题和42.接雨水的题目对应,接雨水是要求栈内元素从大到小排序,然后,取栈顶和栈顶的下一个元素以及要入栈的三个元素来接水!而本题是要求栈内元素从小到大排序,同样,栈顶和栈顶的下一个元素以及要入栈的三个元素来接水!

    请添加图片描述

    然后分析三种情况:

    剩下就是分析清楚如下三种情况:

    • 情况一:当前遍历的元素heights[i]小于栈顶元素heights[st.top()]的情况(出现最大面积)
    • 情况二:当前遍历的元素heights[i]等于栈顶元素heights[st.top()]的情况
    • 情况三:当前遍历的元素heights[i]大于栈顶元素heights[st.top()]的情况

    3.代码

    方法一:动态规划

    public int largestRectangleArea(int[] heights) {
            int length = heights.length;
            int[] minLeftIndex = new int [length];
            int[] maxRigthIndex = new int [length];
            // 记录左边第一个小于该柱子的下标
            minLeftIndex[0] = -1 ;
            for (int i = 1; i < length; i++) {
                int t = i - 1;
                // 这里不是用if,而是不断向右寻找的过程
                while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
                minLeftIndex[i] = t;
            }
            // 记录每个柱子 右边第一个小于该柱子的下标
            maxRigthIndex[length - 1] = length;
            for (int i = length - 2; i >= 0; i--) {
                int t = i + 1;
                while(t < length && heights[t] >= heights[i]) t = maxRigthIndex[t];
                maxRigthIndex[i] = t;
            }
            // 求和
            int result = 0;
            for (int i = 0; i < length; i++) {
                int sum = heights[i] * (maxRigthIndex[i] - minLeftIndex[i] - 1);
                result = Math.max(sum, result);
            }
            return result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    方法二:单调栈

    int largestRectangleArea(int[] heights) {
            Stack<Integer> st = new Stack<Integer>();
            
            // 数组扩容,在头和尾各加入一个元素
            int [] newHeights = new int[heights.length + 2];
            newHeights[0] = 0;
            newHeights[newHeights.length - 1] = 0;
            for (int index = 0; index < heights.length; index++){
                newHeights[index + 1] = heights[index];
            }
    
            heights = newHeights;
            
            st.push(0);
            int result = 0;
            // 第一个元素已经入栈,从下标1开始
            for (int i = 1; i < heights.length; i++) {
                // 注意heights[i] 是和heights[st.top()] 比较 ,st.top()是下标
                if (heights[i] > heights[st.peek()]) {
                    st.push(i);
                } else if (heights[i] == heights[st.peek()]) {
                    st.pop(); // 这个可以加,可以不加,效果一样,思路不同
                    st.push(i);
                } else {
                    while (heights[i] < heights[st.peek()]) { // 注意是while
                        int mid = st.peek();
                        st.pop();
                        int left = st.peek();
                        int right = i;
                        int w = right - left - 1;
                        int h = heights[mid];
                        result = Math.max(result, w * h);
                    }
                    st.push(i);
                }
            }
            return result;
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    4.性能

    方法一:动态规划

    • 时间复杂度:O(n)
    • 空间复杂度:O(n)

    方法二:单调栈

    • 时间复杂度:O(n)
    • 空间复杂度:O(n)
  • 相关阅读:
    【软件与系统安全笔记】七、模糊测试
    JVM【八股文】
    Java“牵手”义乌购商品列表页数据采集+义乌购商品价格数据排序,义乌购API接口申请指南
    了解消息中间件的基础知识
    C# OpenVINO 通用OCR识别 文字识别 中文识别 服务
    双亲委派机制
    springboot高校学生健康档案管理系统java ssm
    深度学习基础算法
    Sqli-labs靶场第17关详解[Sqli-labs-less-17]自动化注入-SQLmap工具注入
    JavaScript定义函数,创建函数实例时的内部原理
  • 原文地址:https://blog.csdn.net/qq_42569028/article/details/125570296