代码随想录训练营 Day49打卡 单调栈 part02 42. 接雨水 84. 柱状图中最大的矩形

代码随想录训练营 Day49打卡 单调栈 part02

一、力扣42. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
**示例 **:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

  1. 单调栈的维护:
    栈中存储的是柱子的下标,用于计算相应的柱子高度。
    栈中柱子的高度是从栈底到栈顶严格递减的(即,栈顶的柱子高度最小)。

  2. 处理三种情况:
    情况一: 当前柱子高度小于栈顶柱子高度,直接将当前柱子的下标入栈,因为栈的顺序需要保持从小到大。
    情况二: 当前柱子高度等于栈顶柱子高度,弹出栈顶元素,并用当前柱子替换栈顶元素,因为相等的情况下需要保留最右边的柱子用于计算宽度。
    情况三: 当前柱子高度大于栈顶柱子高度,此时可以确定一个“凹槽”,通过弹出栈顶元素并计算其与左侧和右侧柱子之间形成的水量。

如果当前遍历的元素(柱子)高度大于栈顶元素的高度,此时就出现凹槽了,如图所示:

版本一

class Solution:
    def trap(self, height: List[int]) -> int:
        # 初始化单调栈(存储柱子的下标)和结果变量
        stack = [0]  # 初始时将第一个柱子的下标加入栈中
        result = 0  # 用于累加每个凹槽的雨水体积
        
        for i in range(1, len(height)):
            # 情况一: 当前柱子高度小于栈顶柱子高度,直接入栈
            if height[i] < height[stack[-1]]:
                stack.append(i)

            # 情况二: 当前柱子高度等于栈顶柱子高度,替换栈顶
            elif height[i] == height[stack[-1]]:
                stack.pop()
                stack.append(i)

            # 情况三: 当前柱子高度大于栈顶柱子高度,处理凹槽并计算雨水体积
            else:
                while stack and height[i] > height[stack[-1]]:
                    # 弹出栈顶元素作为凹槽的底部(即最矮的柱子)
                    mid_height = height[stack[-1]]
                    stack.pop()
                    
                    if stack:
                        # 栈顶元素作为左侧柱子,当前元素作为右侧柱子
                        left_height = height[stack[-1]]
                        right_height = height[i]
                        
                        # 计算凹槽的高度: 两侧较矮的柱子减去凹槽底部的高度
                        h = min(left_height, right_height) - mid_height
                        # 计算凹槽的宽度: 右侧柱子的下标减去左侧柱子的下标减1
                        w = i - stack[-1] - 1
                        
                        # 累加当前凹槽的雨水体积
                        result += h * w
                
                # 当前柱子的下标入栈
                stack.append(i)
        
        return result

版本二 精简

class Solution:
    def trap(self, height: List[int]) -> int:
        # 初始化栈,存放柱子的下标
        stack = [0]
        # 初始化结果变量,用于存储总的雨水体积
        result = 0
        
        # 从下标1开始遍历柱子
        for i in range(1, len(height)):
            # 当当前柱子高度大于栈顶柱子高度时,处理凹槽
            while stack and height[i] > height[stack[-1]]:
                # 弹出栈顶元素,该元素是凹槽的底部高度
                mid_height = stack.pop()
                
                # 检查栈是否为空,若不为空则说明可以形成凹槽
                if stack:
                    # 栈顶元素为左侧柱子的高度,当前遍历到的i为右侧柱子的高度
                    left_height = height[stack[-1]]
                    right_height = height[i]
                    
                    # 计算凹槽的高度: 左右两侧柱子中较矮者 - 凹槽底部的高度
                    h = min(left_height, right_height) - height[mid_height]
                    
                    # 计算凹槽的宽度: 右侧柱子的下标i - 左侧柱子的下标 - 1
                    w = i - stack[-1] - 1
                    
                    # 累加当前凹槽的雨水体积
                    result += h * w
            
            # 当前柱子的下标入栈,继续处理下一个柱子
            stack.append(i)
        
        return result

力扣题目链接
题目文章讲解
题目视频讲解

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

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

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

我们需要在一个柱状图(由高度数组表示)中找到面积最大的矩形。每个柱子的宽度都是1。为了实现这个目标,我们需要找到每个柱子左边和右边第一个比它矮的柱子,这样可以确定以当前柱子为高度的最大矩形的宽度。

举个栗子,如图:

三种情况:
当前柱子高度大于栈顶柱子高度:将当前柱子的索引入栈。
当前柱子高度等于栈顶柱子高度:弹出栈顶柱子的索引,并将当前柱子的索引入栈(因为等高的情况下,只需要保留最新的索引)。
当前柱子高度小于栈顶柱子高度:弹出栈顶柱子,并计算以栈顶柱子为高度的矩形面积,然后将当前柱子的索引入栈。

首尾补0:在heights数组的开头和结尾各插入一个高度为0的柱子,确保所有柱子都能被弹出并计算面积。

首先来说末尾为什么要加元素0?

如果数组本身就是升序的,例如[2,4,6,8],那么入栈之后 都是单调递减,一直都没有走 情况三 计算结果的哪一步,所以最后输出的就是0了。 如图:

结尾加一个0,就会让栈里的所有元素,走到情况三的逻辑。

开头为什么要加元素0?

如果数组本身是降序的,例如 [8,6,4,2],在 8 入栈后,6 开始与8 进行比较,此时我们得到 mid(8),rigt(6),但是得不到 left。
因为 将 8 弹出之后,栈里没有元素了,那么为了避免空栈取值,直接跳过了计算结果的逻辑。

之后又将6 加入栈(此时8已经弹出了),然后 就是 4 与 栈口元素 8 进行比较,周而复始,那么计算的最后结果resutl就是0。 如图所示:

所以我们需要在 height数组前后各加一个元素0。

代码实现

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        # 在高度数组的首尾各添加一个0,这样可以确保所有柱子都能被计算到
        heights.insert(0, 0)
        heights.append(0)
        
        # 初始化栈,栈底存放的是最左边的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.pop()  # 栈顶元素作为矩形的高度
                    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

力扣题目链接
题目文章讲解
题目视频讲解

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值