算法 - 单调栈02(Swift版本)

6 阅读2分钟

题目1:42. 接雨水

讲解

  1. 使用双指针法,找出每根柱子左右两侧最高的柱子。
    然后遍历每根柱子,对于这根柱子可以接到的雨水面积是
    宽度为1
    高度为左右两侧最高柱子里面的较小者 - 这根柱子的高度
  2. 单调栈
    使用递增单调栈
    如果新进栈的元素大于栈顶元素。 那么栈顶元素为柱子底部,新进栈元素为凹槽右侧高度, 栈顶第二个元素为凹槽左侧高度。
    遍历累加高度即可。 这里如果要理解透彻,需要注意到这里的雨水 是一行一行计算面积的,而非双指针的一列一列计算面积。
    因为单调栈寻找的是右侧大于当前位置的柱子,而非最大的柱子。
// 双指针
func trap(_ height: [Int]) -> Int {
    if height.count <= 2 { return 0 }
    var maxLeft = Array(repeating: 0, count: height.count)
    var maxRight = Array(repeating: 0, count: height.count)
    maxLeft[0] = height[0]
    maxRight[height.count - 1] = height[height.count - 1]
    for i in 1..<height.count {
        maxLeft[i] = max(height[i], maxLeft[i - 1])
        maxRight[height.count - 1 - i] = max(height[height.count - 1 - i], maxRight[height.count - i])
    }
    var res = 0
    for i in 1..<(height.count - 1) {
        let water = min(maxLeft[i], maxRight[i]) - height[i]
        if water > 0 { res += water }
    }
    return res
}
    
// 单调栈
func trap(_ height: [Int]) -> Int {
    if height.count <= 2 { return 0 }
    var stack = [0]
    var res = 0
    for i in 1..<height.count {
        while let top = stack.last, height[i] > height[top] {
            stack.removeLast()
            if let left = stack.last {
                let water = (min(height[left], height[i]) - height[top]) * (i - left - 1)
                res += water
            }
        }
        stack.append(i)
    }
    return res
}
    

题目2:84. 柱状图中最大的矩形

讲解

暴力法:
找到柱子左右两侧 最近的比当前小的柱子
然后以当前柱子为高,两侧柱子距离为宽 求面积。

单调栈:
递减单调栈
栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度

另外这里有个细节:
数组左右两次需要补充个0,可以比较方便的适配单调递增或者递减的情况。

class Solution {
    func largestRectangleArea(_ heights: [Int]) -> Int {
        // 基本思路:找到每根柱子, 左右两侧离他最近且小于它的柱子, 然后求面积, 遍历取最大值。
        // 递减单调栈 
        // 前后补0,  适配 完全递增 或者 完全递减的情况。
        // 栈顶 到 栈底 为递减。 来个较小值, 就可以找到栈顶元素的左右 第一个小于它的值。
        var hs = [0]
        hs.append(contentsOf: heights)
        hs.append(0)
        var stack = [0]
        var res = 0
        for i in 1..<hs.count {
            while let top = stack.last, hs[i] < hs[top] {
                stack.removeLast()
                if let left = stack.last {
                    let w = i - left - 1
                    res = max(res, w * hs[top])
                }
            }
            stack.append(i)
        }
        return res
    }
}