LeetCode42 接雨水

0 阅读2分钟

leetcode.cn/problems/tr…

image.png

解法一:暴力法

对于位置 i,能够装的水为:

water[i] = min( // 盛水的高度取决于左右柱子的较小值 
    // 左边最高的柱子 
    max(height[0..i]), 
    // 右边最高的柱子 
    max(height[i..end]) 
) - height[i]

image.png image.png

遍历每个位置,都计算一遍可以装的水量,求和即可。

func trap(height []int) int {
    res := 0
    for i:=0; i<len(height); i++{
        l_max, r_max := 0, 0
        // 找左边最高的柱子
        for j := i; j>=0; j--{
            l_max = max(l_max, height[j])
        }
        // 找右边最高的柱子
        for j := i; j<len(height); j++{
            r_max = max(r_max, height[j])
        }
        cur := min(l_max, r_max) - height[i]
        res += cur
    }
    return res
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

解法二:备忘录优化

直接把左右最高柱子的高度都提前计算出来,就不需要在每个位置 i 都要计算 r_max 和 l_max,避免了重复计算

func trap(height []int) int {
    if len(height) == 0{
        return 0
    }
    // 申请两个数组充当备忘录
    l_max := make([]int, len(height))
    r_max := make([]int, len(height))
    // base case
    l_max[0] = height[0]
    r_max[len(r_max)-1] = height[len(height)-1]
    // 从左向右计算每个位置 l_max
    for i := 1; i < len(l_max); i++ {
        l_max[i] = max(height[i], l_max[i-1])
    }
    // 从右向左计算每个位置 r_max
    for j := len(r_max)-2; j>=0; j--{
        r_max[j] = max(height[j], r_max[j+1])
    }
    // 计算接水量
    res := 0
    for i := 0; i <len(height); i++{
        res += min(l_max[i], r_max[i]) - height[i]
    }
    return res
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

解法三:双指针

不需要用备忘录提前计算了,而是用双指针边走边算,节省下空间复杂度。

func trap(height []int) int {
    if len(height) == 0{
        return 0
    }
    left, right := 0, len(height)-1
    l_max, r_max := 0, 0

    res := 0
    for left < right{
        // l_max 记录 height[0...left] 中最高柱子的高度
        l_max = max(l_max, height[left])
        // r_max 记录 height[right...end] 的最高柱子的高度
        r_max = max(r_max, height[right])
        // 取左右高度较小的那个柱子计算
        if l_max < r_max {
            res += l_max - height[left]
            left++
        }else {
            res += r_max - height[right]
            right--
        }
    }
    return res
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

分析: 对于下图,此时的 l_max 是 left 指针左边的最高柱子,但是 r_max 并不一定是 left 指针右边最高的柱子,这真的可以得到正确答案吗? image.png 其实这个问题要这么思考,我们只在乎 min(l_max, r_max)对于上图的情况,我们已经知道 l_max < r_max 了,至于这个 r_max 是不是右边最大的,不重要。重要的是 height[i] 能够装的水只和较低的 l_max 之差有关