创见博客
接雨水
七崽爱吃小饼干2025/12/25阅读 1专栏 算法合集

接雨水

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

示例 1:

codeType
输入: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:

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

提示:

codeType
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105

解法一:

暴力法 + 贪心。这题和发糖果那题很像(同样是需要同时满足左规则和右规则),位置i的接雨水的最高高度为位置i的左边最高墙和右边最高墙中矮的那个。所以只要求出位置i的左边最高墙和右边最高墙,就能得到答案。每个位置找左边最高墙和右边最高墙需要遍历一遍数组,所以时间复杂度是O(n)。n个位置找n遍,所以最终的时间复杂度时O(n^2)。

以下题解存在很多可以优化的地方。写这么臃肿主要是为了代码更直观更好懂。

typescript
function trap(height: number[]): number {
    // 暴力法
    // 位置i的左面最高墙为L[i],右面最高墙为R[i] (其实就是假设右边无限高/左边无限高,感觉和发糖果那题很像)
    // 那么位置i接水后所能达到的最高高度h[i]就是,min(L[i], R[i])
    // 那么位置i的水量就是最高高度-位置i的高度也就是max(h[i] - height[i],0) 不能小于0
    // 所以只要得到数组L和数组R就能解答
    const n = height.length
    const L: number[] = new Array(n).fill(0)
    const R: number[] = new Array(n).fill(0)
    const res: number[] = new Array(n).fill(0)
    L[0] = 0
    for(let i=1; i<n; i++){
        let LeftMax = 0
        // 求位置i的L数组
        for(let j=0; j<i; j++){
            // 找位置i左边的最大值,从0开始遍历到i-1
            LeftMax = Math.max(LeftMax, height[j])
        }
        L[i] = LeftMax
    }
    R[n-1] = 0
    for(let i=n-2; i>=0; i--){
        let RightMax = 0
        for(let j=n-1; j>i; j--){
            // 找位置i右边的最大值,从n-1开始遍历到i+1
            RightMax = Math.max(RightMax, height[j])
        }
        R[i] = RightMax
    }
    for(let i=0; i<n; i++){
        res[i] = Math.max(Math.min(L[i], R[i]) - height[i], 0) // 每个位置能接的雨水量
    }
    let sum = 0
    res.forEach(num => {
        sum += num
    })
    return sum
};

解法二:

动态规划:上一个解法当中存在很多可以优化的地方,特别是求L数组和R数组的过程,这个过程中存在很多重复的操作,可以用动态规划的方式进行优化。观察下面的解题过程我们会发现,求L的时候,L[i] = Max(L[i-1), height[i-1]),也就是说,求L[i]的时候并不需要遍历整个height。R数组同理。求L和R的时间复杂度被降低到O(n),所以整体的时间复杂度就降到了O(n)

typescript
function trap(height: number[]): number {
    // 动态规划
    const n = height.length
    const L: number[] = new Array(n).fill(0)
    const R: number[] = new Array(n).fill(0)
    const res: number[] = new Array(n).fill(0)
    L[0] = 0
    for(let i=1; i<n; i++){
        L[i] = Math.max(L[i-1], height[i-1]) // 简化了求L和R的过程
    }
    R[n-1] = 0
    for(let i=n-2; i>=0; i--){
        R[i] = Math.max(R[i+1], height[i+1])
    }
    for(let i=0; i<n; i++){
        res[i] = Math.max(Math.min(L[i], R[i]) - height[i], 0) // 每个位置能接的雨水量
    }
    let sum = 0
    res.forEach(num => {
        sum += num
    })
    return sum
};
评论
0/100