• Leetcode 84.柱状图中最大的矩形


    1.题目描述

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

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

    在这里插入图片描述

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

    输入: heights = [2,4]
    输出: 4


    提示:

    • 1 <= nums1.length <= nums2.length <= 1000
    • 0 <= nums1[i], nums2[i] <= 104
    • nums1和nums2中所有整数 互不相同
    • nums1 中的所有整数同样出现在 nums2 中

    2.思路分析

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

    2.1 动态规划

    对于下标i而言,能勾勒出的最大面积是什么?

    以i为中心, 向左寻找第一个小于height[i]的位置leftMin, 向右寻找第一个小于height[i]的rightMin, 即最大面积=height[i] *(rightMin - leftMin - 1)

    举个栗子:heights = [2,1,5,6,2,3],对于下标5(元素2)而言:
    在这里插入图片描述

    • 定义两个长度为n的数组leftMin和rightMin

      • leftMin[i]:左边第一个小于下标i的柱子的下标
      • rightMin[i]:右边第一个小于下标i柱子的下标
    • leftMin[0] = -1 rightMin[n-1] = n

    • 正向遍历数组 height 得到数组 leftMin 的每个索引值(第一小于当前柱子高度的索引值),反向遍历数组 height 得到数组rightMin (第一小于当前柱子高度的索引值)

    • 遍历结束之后,下标i处能勾勒出的最大面积:= heights[i] * (righMin[i] -leftMin[i] - 1)

    2.2 单调栈

    Leetcode 42.接雨水是找每个柱子左右两边第一个大于该柱子高度的柱子,而本题是找每个柱子左右两边第一个小于该柱子的柱子。

    Q1:单调栈内的元素顺序?

    维护一个单调栈, 单调栈存储的元素的下标, 栈顶->栈底:递增(小->大)

    一旦发现添加的柱子高度小于栈顶元素,此时就会出现凸起, 栈顶元素就是凸起顶部的柱子, 栈顶的第二个元素就是凸起左边的柱子, 当前元素就是凸起右边的柱子。
    在这里插入图片描述

    Q2:遇到相同柱子如何处理?

    遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。

    单调栈的处理逻辑:

    • 情况1:当前遍历元素高度 > 栈顶元素的高度, 当前元素入栈(保持 大>小 单调的性质 )

    • 情况2: 当前遍历元素高度 == 栈顶元素高度, 更新栈顶元素(遇到相同柱子,使用右边柱子计算高度)

    • 情况3: 当前遍历元素高度 < 栈顶元素, 此时出现凸起, 弹出栈顶元素(凸起顶部柱子, 记为mid), 此时

      栈顶元素(stack.top())(凸起左边的柱子, height[st.top()]), 当前元素记为凸起右边的柱子(height[i])

      • 矩形高度: w = heights[i]
      • 矩形宽度: l = i - stack.top() -1
      • 矩形的面积:l * w

    3.代码实现

    3.1 动态规划

    class Solution:
        def largestRectangleArea(self, heights: List[int]) -> int:
            # 动态规划
            if not heights:
                return 0
            n = len(heights)
            # 两个数组存储的下标
            leftMin = [0] * n
            rightMin = [0] * n
            result = 0
    
            leftMin[0], rightMin[n-1] = -1, n
    
            # 正向遍历数组
            for i in range(1, n):
                temp = i - 1
                while temp >= 0 and heights[temp] >= heights[i]:
                    # 寻找次级柱子
                    temp = leftMin[temp]
                # 寻找到左侧第一个小于当前柱子高度的下标
                leftMin[i] = temp
            # 反向遍历数组
            for i in range(n - 2, -1, -1):
                temp = i + 1
                while temp < n and heights[temp] >= heights[i]:
                    # 寻找次级柱子
                    temp = rightMin[temp]
                rightMin[i] = temp
            for i in range(n):
                area = heights[i] * (rightMin[i] - leftMin[i] - 1)
                result = max(area, 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
    • 28
    • 29
    • 30
    • 31
    • 32

    复杂度分析

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

    3.2 单调栈

    # 方式1
    class Solution:
        def largestRectangleArea(self, heights: List[int]) -> int:
            heights.insert(0, 0)
            heights.append(0)
            stack = [0]
            result = 0
            for i in range(1, len(heights)):
                # 情况一
                if heights[i] > heights[stack[-1]]:
                    stack.append(i)
                # 情况二
                elif heights[i] == heights[stack[-1]]:
                    stack.pop()
                    stack.append(i)
                # 情况三
                else:
                    # 抛出所有较高的柱子
                    while stack and heights[i] < heights[stack[-1]]:
                        # 栈顶就是中间的柱子,主心骨
                        mid_index = stack[-1]
                        stack.pop()
                        if stack:
                            left_index = stack[-1]
                            right_index = i
                            width = right_index - left_index - 1
                            height = heights[mid_index]
                            result = max(result, width * height)
                    stack.append(i)
            return result
    # 方式2
    class Solution:
        def largestRectangleArea(self, heights: List[int]) -> int:
            heights.insert(0, 0)
            heights.append(0)
            stack = [0]
            result = 0
            for i in range(1, len(heights)):
                while stack and heights[i] < heights[stack[-1]]:
                    mid_height = heights[stack[-1]]
                    stack.pop()
                    if stack:
                        # area = width * height
                        area = (i - stack[-1] - 1) * mid_height
                        result = max(area, result)
                stack.append(i)
            return result
    # 方式3:
    class Solution:
        def largestRectangleArea(self, heights: List[int]) -> int:
            if not heights:
                return 0
            heights.insert(0, 0)
            heights.append(0)
            stack = []
            result = 0
    
            for i in range(len(heights)):
                while stack and heights[i] <= heights[stack[-1]]:
                    height = heights[stack.pop()]
                    if stack:
                        w = i - stack[-1] - 1
                        result = max(result, height * w)
                stack.append(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
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    复杂度分析

    • 时间复杂度:O(n)。
    • 空间复杂度:O(n)。
  • 相关阅读:
    FITC-PEG-MAL,荧光素聚乙二醇马来酰亚胺,FITC-PEG-Maleimidev
    enote笔记法之附录2——5w1h2k关联词
    MPLAB X IDE 仿真打断点提示已中断的断点?
    SignalR向部分客户端进行消息推送
    国庆中秋宅家自省:偷偷尝鲜
    SVN如何删除文件名包含空格的文件
    双十一特辑-北汇在C站的两周年打卡纪念:)
    代码随想录算法训练营第51天 | 714.买卖股票的最佳时机含手续费 309.最佳买卖股票时机含冷冻期 300.最长递增子序列
    华为云云服务器评测|在云耀云服务器L实例上部署battle-city坦克大战小游戏
    UnityShader入门精要——PBS基于物理的渲染
  • 原文地址:https://blog.csdn.net/weixin_44852067/article/details/126606349