# 5.4 如何高效解决接雨水问题

本文对应的力扣题目:

42.接雨水

说白了就是用一个数组表示一个条形图,问你这个条形图最多能接多少水,函数签名如下:

int trap(int[] height);
1

# 5.4.1 核心思路

暴力算法:

int trap(vector<int>& height) {
    int n = height.size();
    int ans = 0;
    for (int i = 1; i < n - 1; i++) {
        int l_max = 0, r_max = 0;
        // 找右边最高的柱子
        for (int j = i; j < n; j++)
            r_max = max(r_max, height[j]);
        // 找左边最高的柱子
        for (int j = i; j >= 0; j--)
            l_max = max(l_max, height[j]);
        // 计算能够装的水
        ans += min(l_max, r_max) - height[i];
    }
    return ans;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 5.4.2 备忘录优化

预先把这两个数组计算好,即可避免重复计算:

int trap(vector<int>& height) {
    if (height.empty()) return 0;
    int n = height.size();
    int ans = 0;
    // 数组充当备忘录
    vector<int> l_max(n), r_max(n);
    // 初始化 base case
    l_max[0] = height[0];
    r_max[n - 1] = height[n - 1];
    // 从左向右计算 l_max
    for (int i = 1; i < n; i++)
        l_max[i] = max(height[i], l_max[i - 1]);
    // 从右向左计算 r_max
    for (int i = n - 2; i >= 0; i--) 
        r_max[i] = max(height[i], r_max[i + 1]);
    // 计算答案
    for (int i = 1; i < n - 1; i++) 
        ans += min(l_max[i], r_max[i]) - height[i];
    return ans;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 5.4.3 双指针解法

int trap(vector<int>& height) {
    if (height.empty()) return 0;
    int n = height.size();
    int left = 0, right = n - 1;
    int ans = 0;
    
    int l_max = height[0];
    int r_max = height[n - 1];
    
    while (left <= right) {
        l_max = max(l_max, height[left]);
        r_max = max(r_max, height[right]);
        
        // ans += min(l_max, r_max) - height[i]
        if (l_max < r_max) {
            ans += l_max - height[left];
            left++; 
        } else {
            ans += r_max - height[right];
            right--;
        }
    }
    return ans;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

接雨水

var trap = function(height){
    // 初始化能接雨水量
    let ans = 0;
    // 初始化两个指针,左边为0,右边为柱高长度-1
    let left = 0, right = height.length - 1;
    // 初始化两边存储最大值
    let leftMax = 0, rightMax = 0;
    // 当两个指针没有相遇的时候
    while(left < right){
        // 更新两边存储最大值
        leftMax = Math.max(leftMax, height[left]);
        rightMax = Math.max(rightMax, height[right]);
        // 如果 左边柱子高度 小于 右边
        if(height[left] < height[right]){
            // 下标 left 处能接的雨水量 = 左边存储的最大值 - 左边柱子高度
            ans += leftMax - height[left];
            // 移动左指针
            ++left;
        }else{
            ans += rightMax -  height[right];
            --right;
        }
    }
    return ans;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

繁琐问题必有猥琐解法,记录简单处理问题的逻辑,就能掌握规律!

最后更新时间: 6/20/2022, 10:48:50 PM