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>& nums) {
        int n = nums.size();
        if(n==0) return 0;
        int left = nums[0], right = nums[n-1];
        int l=0, r=n-1, ans = 0;
        while(l<r){
            if(nums[l]<=nums[r]){
                ++l;
                ans += max(0,min(left,right)-nums[l]);
                left = max(left,nums[l]);

            }
            else{
                --r;
                ans += max(0,min(left,right)-nums[r]);
                right = max(right,nums[r]);
            }
        }
        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;
				
				// 宽度 d = 当前索引减去栈顶索引再减1
                int d = cur-st.top()-1;
                // 高度 h = 当前和栈顶索引所对应高度的较小值(也就是左右高度的较小值)减去当前高度top
                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_right += left_max;
            right_max = max(right_max,height[n-1-i]);
            s_left += 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/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值