42. 接雨水

Origin: LeetCode 42

题目描述

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

示例

Input: height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] Output: 6

Input: height = [4, 2, 0, 3, 2, 5] Output: 9

约束

  • 1 <= height.length <= 3 × 10
  • 0 <= height[i] <= 10

关键信息

  • 每根柱子上能接的水 = min(左边最高, 右边最高) - 当前高度
  • 双指针 O(n) 或 预处理左右最大值数组 O(n)
  • 单调栈也能解

Resolution

我的解法

function trap(height: number[]): number {
    let res = 0
    const n = height.length
 
    const leftMax: number[] = Array.from({ length: n }, () => 0)
    leftMax[0] = height[0]
    for (let i = 1; i < height.length; i++) {
        leftMax[i] = Math.max(leftMax[i - 1], height[i])
    }
 
    const rightMax: number[] = Array.from({ length: n }, () => 0)
    rightMax[n - 1] = height[n - 1]
    for (let i = n - 2; i >= 0; i--) {
        rightMax[i] = Math.max(rightMax[i + 1], height[i])
    }
 
    for (let i = 1; i < n - 1; i++) {
        res += Math.min(leftMax[i], rightMax[i]) - height[i]
    }
    return res
}

暴力解法(超时,O(n²))

function trap(height: number[]): number {
    let res = 0
    for (let i = 1; i < height.length - 1; i++) {
        const leftMax = Math.max(...height.slice(0, i))
        const rightMax = Math.max(...height.slice(i + 1))
        const min = Math.min(leftMax, rightMax)
        if (leftMax > height[i] && rightMax > height[i]) {
            const curHeight = min - height[i]
            res += curHeight
        }
    }
    return res
}

每次 Math.max(...height.slice()) 重新扫描整个区间,O(n²) 超时。思路对,只是没预处理。

解题思路

每根柱子能接的水 = min(左侧最高, 右侧最高) - 当前高度。预处理两个数组:

  • leftMax[i]:0 到 i 的最高值,从左到右扫一遍
  • rightMax[i]:i 到 n-1 的最高值,从右到左扫一遍

最后遍历算水:min(leftMax[i], rightMax[i]) - height[i]

踩坑

  • 暴力法 Math.max(...height.slice(0, i)) 每次重新扫描,O(n²) 超时
  • 预处理数组空间换时间,查的时候 O(1)

复杂度

  • 时间:O(n),三次遍历
  • 空间:O(n),两个预处理数组