day49 单调栈2

99 阅读2分钟

42. 接雨水

文章讲解

思路1:

从左往右,从右往左遍历,分别记录i位置的左右比他高的柱子的最大高度l_max[i]和r_max[i],高度为min(l_max[i], r_max[i]),水容量为min(l_max[i], r_max[i])-height[i]

class Solution:
    def trap(self, height: List[int]) -> int:
        if not height:
            return 0
        # 记录左右侧的最大值
        n = len(height)
        l_max = [0] * len(height)
        r_max = [0] * len(height)
        l_max[0] = height[0]
        r_max[-1] = height[-1]

        # 更新l_max
        for i in range(1, n):
            l_max[i] = max(height[i], l_max[i-1])
        # 更新r_max
        for i in range(n-2,-1,-1):
            r_max[i] = max(height[i], r_max[i+1])

        # 计算容积(从柱子1开始蓄水,并且最后一个只能蓄0,不用管)
        result = 0
        for i in range(1, n):
            result += min(l_max[i], r_max[i]) - height[i]
        
        return result

思路2:

单调栈从栈顶到栈底部从小到大放元素(金字塔),遇到比栈顶大的元素时候,栈顶元素stack[-1]比较小,然后他下面的stack[-2]和当前元素i都比他大,计算stack[-1]的水容量。
< 加入i
= 弹出top,加入i
> 弹出top,更新答案,加入i
class Solution:
    def trap(self, height: List[int]) -> int:
        # base case
        if not height:
            return 0
        
        # 高度需要找到左边第一个比他高的右边第一个比他高的。
        result = 0
        stack = [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 = stack[-1]
                    stack.pop()
                    if stack:
                        r_h = height[i]
                        l_h = height[stack[-1]]
                        h = min(r_h, l_h) - height[mid]
                        w = i - stack[-1] - 1 # 中间宽度
                        result += h * w
                stack.append(i)
        return result

84. 柱状图中最大的矩形

文章讲解

思路:

找到柱子左右侧比他矮的两根柱子leftright,区间内的柱子都比他高,宽度为r-l-1(不包含左右的柱子)
高度是mid;从栈顶到栈底从大到小排列,找到周围比他小的元素。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        heights.insert(0,0) # 第一个和最后一个柱子可以算面积1*height,而接雨水首位柱子不能接
        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 = stack[-1]
                    stack.pop()
                    if stack:
                        left = stack[-1]
                        right = i
                        width = right - left -1
                        height = heights[mid]
                        result = max(result, width * height)
                    
                stack.append(i)
        return result

简化

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 = stack[-1]
            stack.pop()
            if stack:
                left = stack[-1]
                right = i
                width = right - left -1
                height = heights[mid]
                result = max(result, width * height)

        stack.append(i)
    return result