LeetCode 热题 HOT 100(双指针)42. 接雨水

55 阅读3分钟

题目描述

42. 接雨水

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

 

示例 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 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入: height = [4,2,0,3,2,5]
输出: 9

 

提示:

  • n == height.length
  • 1 <= n <= 2 * 10^4
  • 0 <= height[i] <= 10^5

思路1

将每个位置看做一个桶,每个桶所能容纳多少单位的水取决于这个桶短板的高度和这个桶本身柱子高度的差。而每个桶拥有左右两个木板,而且它的实际值取决于左侧木板最大值和右侧木板的最大值。因为如果柱子高于这些木板最大值,那么水就会流出去,否则就不会流出去。因此我们只要拿到每个桶的左右侧木板最大高度然后取小者再减去柱子高度就能够得到这个桶容纳水单位数量,那么所有的桶容纳水单位数量之和就是结果了。

将每个位置看做是一个桶,这个桶所能容纳水的单位取决于桶左右两个挡板中较小的值减去柱子本身的高度,因为柱子一旦高于这个值,那么水就会流走,否则就会被两个挡板夹住。

func trap(height []int) int {
    n := len(height)
    prefix, suffix := make([]int, n), make([]int, n)
    prefix[0], suffix[n-1] = height[0], height[n-1]
    for i := 1; i < n; i++ {
        prefix[i] = max(prefix[i-1], height[i])
    }
    for i := n-2; i >= 0; i-- {
        suffix[i] = max(suffix[i+1], height[i])
    }

    var totalRainFall int
    for i := 0; i < n; i++ {
        totalRainFall += min(prefix[i], suffix[i]) - height[i]
    }
    return totalRainFall
}

时间复杂度:写了3个for循环,每个都是 O(n),所以是 O(n)

空间复杂度:O(n)

思路2

对于某个桶,假设已知左侧木板高度是1,而右侧木板高度是3,此时即使我们不知道实际右侧木板最大高度是多少,但是它至少就已经是3了。而由于桶盛水单位数量取决于短木板的高度,因此此时就可以计算出这个桶的盛水的单位数量(左侧木板最大高度减去当前位置的柱子高度)。与此同时,可以同步更新左侧木板的最大高度了,再使用新的木板最大高度计算下一个位置。在下一个位置,可能是右侧木板高度更小,那么此时可以确定的是左侧木板高度至少已经是当前值,实际盛水单位数取决于右侧木板最大高度,因此可以计算出右侧位置的盛水单位数,更新右侧木板最大高度,一直到两个指针相遇。

func trap(height []int) int {
    n := len(height)
    left, right := 0, n-1
    leftMaxBoard, rightMaxBoard, totalRainFall := 0, 0, 0
    for left < right {
        leftMaxBoard = max(leftMaxBoard, height[left])
        rightMaxBoard = max(rightMaxBoard, height[right])
        if leftMaxBoard > rightMaxBoard {
            totalRainFall += rightMaxBoard-height[right]
            right--
        } else {
            totalRainFall += leftMaxBoard-height[left]
            left++
        }
    }
    return totalRainFall
}

时间复杂度:O(n)

空间复杂度:O(1)