LeetCode 42. 接雨水(双指针巧妙求解,韦恩图拍案叫绝)

2021年03月13日 周六 天气晴 【不悲叹过去,不荒废现在,不惧怕未来】


1. 问题简介

42. 接雨水
在这里插入图片描述

2. 多种解法(数学法最为巧妙)

具体可参考:https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/

2.1 按列求(循序渐进)

2.1.1 暴力法(双循环)

暴力法:对数组进行遍历,每次都用循环计算左右两边的边界值,然后获得这一列的水量。

暴力法虽然时间复杂度高,为O(n*n),但是理解暴力法后,就可以对其进行改进,从而得到更好的方法。

class Solution {
    
    
public:
    int trap(vector<int>& height) {
    
       
        const int n = height.size();
        int res = 0;
        for(int i=1;i<n-1;++i){
    
    
            int max_left = 0, max_right = 0;

            for(int j=i;j>=0;--j)
                max_left = max(max_left,height[j]);
            for(int j=i;j<n;++j)
                max_right = max(max_right,height[j]);
                
            res += min(max_left,max_right)-height[i];
        }
        return res;
    }
};

2.1.2 暴力法优化一——动态规划

暴力法在计算左右两边的边界值时,存在大量的重复计算,所以可以用动态规划存储中间结果,将时间复杂度降为O(n)

class Solution {
    
    
public:
    int trap(vector<int>& height) {
    
       
        if(height.empty()) return 0;
        const int n = height.size();
        int ans = 0;
        vector<int> max_left(n), max_right(n);
        max_left[0] = height[0];
        max_right[n-1] = height[n-1];

        for(int i=1;i<n;++i)
            max_left[i] = max(max_left[i-1],height[i]);
        for(int i=n-2;i>=0;--i)
            max_right[i] = max(max_right[i+1],height[i]);
        for(int i=0;i<n;++i)
            ans += min(max_left[i],max_right[i])-height[i];

        return ans;
    }
};

2.1.3 暴力法优化二——双指针,巧妙求解

双指针法也是暴力法的改进,其突出一个左右横跳,左右两边哪边高就去遍历另一边,计算正在遍历的列的存水量,这样一趟遍历下来,结果也就出来了,并且只需要常数空间。

class Solution {
    
    
public:
    int trap(vector<int>& height) {
    
    
        if(height.empty()) return 0;
        int i = 0, j = height.size()-1;
        int left_max = height[i], right_max = height[j];
        int ans = 0;
        while(i<j){
    
    
            if(height[i]<=height[j]){
    
    
                ++i;
                left_max = max(left_max,height[i]);
                if(height[i]<left_max)
                ans += left_max-height[i];    
            }
            else {
    
    
                --j;
                right_max = max(right_max,height[j]);
                if(height[j]<right_max)
                ans += right_max-height[j];    
            }
        }
        return ans;
    }
};

2.2 按行求(单调栈)

单调栈是按行来求解,思路比较难理解一些,总体原则是:

  • 当前高度小于等于栈顶高度时,入栈,指针后移。

  • 当前高度大于栈顶高度时,出栈,然后计算当前栈顶和当前高度所围成区域的行储水量,直到当前墙的高度小于等于栈顶高度或者栈空,然后把当前墙入栈,指针后移。

class Solution {
    
    
public:
    int trap(vector<int>& height) {
    
    
        const int n = height.size();
        stack<int> st;
        int res = 0, cur = 0;
        while(cur<n){
    
    
            while(!st.empty() && height[st.top()]<height[cur]){
    
    
                int top = height[st.top()];
                st.pop();
                if(st.empty()) break;

                int d = cur-st.top()-1;
                int h = min(height[cur],height[st.top()])-top;
                res += d*h;
            }
            st.push(cur++);
        }
        return res;
    }
};

2.3 数学法(韦恩图,拍案叫绝)

这种方法是无意间在leetcode题解上看到的:https://leetcode-cn.com/problems/trapping-rain-water/solution/wei-en-tu-jie-fa-zui-jian-dan-yi-dong-10xing-jie-j/,利用韦恩图来求解,可谓是脑洞大开了,并且也只需要常数的空间复杂度。

s_right:假设最右边是封闭的,能接到的雨水量
在这里插入图片描述
s_left:假设最左边是封闭的,能接到的雨水量
在这里插入图片描述
s_rect:假设两边都是封闭的,能接到的雨水量,也就是整个矩形的面积

s_zhu :柱子的面积
在这里插入图片描述

由上图阴影区域可知:s_left + s_right - s_rect = s_zhu + res

class Solution {
    
    
public:
    int trap(vector<int>& height) {
    
    
        if(height.empty()) return 0;
        const int n = height.size();
        int left_max = height[0], right_max = height[n-1];
        // s_left:假设最左边是封闭的,能接到的雨水量
        // s_right:假设最右边是封闭的,能接到的雨水量
        // s_rect:假设两边都是封闭的,能接到的雨水量,也就是矩形的面积
        // s_left + s_right - s_rect = s_zhu + res
        int s_left = 0, s_right = 0, s_zhu = 0, h_max = height[0];
        for(int i=0;i<height.size();++i){
    
    
            left_max = max(left_max,height[i]);
            s_left += left_max;
            right_max = max(right_max,height[n-1-i]);
            s_right += right_max;

            h_max = max(h_max,height[i]);   
            s_zhu += height[i];
        }
        int s_rect = h_max*n;
        return s_left + s_right - s_zhu - s_rect;
    }
};

参考文献

https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/

https://leetcode-cn.com/problems/trapping-rain-water/solution/wei-en-tu-jie-fa-zui-jian-dan-yi-dong-10xing-jie-j/

猜你喜欢

转载自blog.csdn.net/m0_37433111/article/details/114736417