LeetCode [One hard A Day] - 42. Trapping Rain Water

Trapping Rain Water

对于array里的每一个位置来说,它的积水量 = min(左边最高的墙, 右边最高的墙) — 自己位置的高度。[哭了,我连这个都不知道,感觉输在起跑线上了]

总结,这道题其实就是寻找每个位置的
基准:min(左边最高的墙, 右边最高的墙) <—— 重点在于如何寻找基准 [min of two side max]
自高:自己位置的高度

Brute Force:
基准:遍历左侧找到左边最高墙,遍历右侧找到右边最高墙
遍历每一个位置,在每一个位置,用上面的方法寻找基准,并计算积水量。最后将所有的积水量加起来即可。

有什么办法可以简化吗?

计算2左侧的最高墙,需要对比0,1的高度
计算3左侧的最高墙,需要对比0,1,2的高度
[我听到了DP的召唤]

解法一 DP

基准:基于左侧一位的最高左墙找到当前最高左墙,基于右侧一位的最高右墙找到当前最高右墙。
第一次从左往右遍历,存入每位左侧最高的数;第二次从右往左遍历,先得到右测最高的数,并与之前的比较得到最小值,最后与自己位数上的数比较,累计到最终答案上。

Use Two Arrays

class Solution {
public:
    int trap(vector<int>& height) {
        int len = height.size();
        if(len<3) return 0;
        vector<int> leftH(len, 0);
        vector<int> rightH(len, 0);
        int res=0, mx = 0;
        for(int i=0;i<len;i++){
            leftH[i] = mx;
            mx = max(mx, height[i]);
        }
        mx = 0;
        for(int i=len-1;i>=0;i--){
            rightH[i] = mx;
            mx = max(mx, height[i]);
            if(min(rightH[i], leftH[i])>height[i]){
                res += min(rightH[i], leftH[i])-height[i];
            }
        }
        return res;
    }
};

Common Mistake

Correct: int res=0, mx = 0;
Wrong:   int res, mx = 0;

Use One Array

每个位置上的右侧最高值不会再用到了,所以不需要用另一个array rightH来存。

class Solution {
public:
    int trap(vector<int>& height) {
        int len = height.size();
        if(len<3) return 0;
        vector<int> leftH(len, 0);
        int res=0, mx = 0;
        for(int i=0;i<len;i++){
            leftH[i] = mx;
            mx = max(mx, height[i]);
        }
        mx = 0;
        for(int i=len-1;i>=0;i--){
            if(min(mx, leftH[i])>height[i]){
                res += min(mx, leftH[i])-height[i];
            }
            mx = max(mx, height[i]);
        }
        return res;
    }
};

不用mx

class Solution {
public:
    int trap(vector<int>& height) {
        int len = height.size();
        if(len<3) return 0;
        vector<int> leftH(len, 0);
        int res=0;
        leftH[0] = height[0];
        for(int i=1;i<len-1;i++){
            leftH[i] = max(height[i], leftH[i-1]);
        }
        leftH[len-1] = height[len-1];
        for(int i=len-2;i>0;i--){
            int rightH = max(height[i], leftH[i+1]);
            res += min(rightH, leftH[i])-height[i];
            leftH[i] = rightH;
        }
        return res;
    }
};

Back to the Beginning: 遍历每一个位置,在每一个位置,找到左边最高墙和右边最高墙,并计算积水量。我们真的需要左边最高墙和右边最高墙吗?我们真正需要的是两者的最小值。
What! How these two are different?
左边最高墙和右边最高墙:需要遍历当前位置的所有左侧墙和右侧墙
两者的最小值:知道一侧的最高墙,并且知道此墙低于另一侧的一面墙,那么这一侧的最高墙就是我们寻找的两者最小值。举个例子,height[0]<height[len-1],那么我们就知道对于height[1]来说,height[0]就是左侧最高墙,右侧最高墙并不重要的了,因为肯定高于height[0],所以position 1形成的坑就是height[0]-height[1] if height[0]>height[1]。
总结一下,我们到底需要什么?
基准:遍历一侧得到最高墙,并且知道另一侧的一面墙高于此墙。[哈哈,two pointers 两边向中间遍历~]

解法二 Two Pointers

Continue with the above example:
if height[0]>height[1]: height[0] will still be [the max of the left side] and [the min of the max left and max right]
if height[0]<height[1]: height[1] will replace height[0] to be [the max of the left side] and [the min of the max left and max right] requires an additional comparison.

class Solution {
public:
    int trap(vector<int>& height) {
        int len = height.size();
        if(len<3) return 0;
        int l=0, r=len-1, res=0;
        while(l<r){
            int mn = min(height[l], height[r]);
            if(mn==height[l]){
                l++;
                while(l<r && mn>height[l]){
                    res += mn-height[l++];
                }
            }else{
                r--;
                while(l<r && mn>height[r]){
                    res += mn-height[r--];
                }
            }
        }
        return res;
    }
};

优化

基准:取两侧较小的一面墙,如果大于基准,update。

class Solution {
public:
    int trap(vector<int>& height) {
        int len = height.size();
        if(len<3) return 0;
        int l=0, r=len-1, res=0, level=0;
        while(l<r){
            int curH = height[height[l] < height[r] ? l++: r--];
            level = max(level, curH);
            res += level - curH;
            
        }
        return res;
    }
};

解法三 Stack

之前我们都是每一位每一位竖向思考,那么现在我们来尝试一下横向思考。
水坑是怎么形成的?左半边会有一个下降的趋势,然后当右半边升高时,每升高一层,就会形成一层的水坑。
总结,每次算一个大水坑的大小。运用stack存左半边所有墙,遍历右半边所有墙的时候一层层算出坑的大小。

class Solution {
public:
    int trap(vector<int>& height) {
        int res =0, i=0, n=height.size();
        stack<int> st;
        while(i<n){
            if(st.empty() || height[i]<=height[st.top()]){
                st.push(i++);
            }else{
                int t=st.top(); st.pop();
                if(st.empty()) continue;
                res += (min(height[i], height[st.top()])-height[t])*(i-st.top()-1);
            }
        }
        return res;
    }
};

Reference:http://www.cnblogs.com/grandyang/p/4402392.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值