Trapping Rain Water

Date:

  Nov. 2, 2017

Problem:

  https://leetcode.com/problems/trapping-rain-water/description/

Description:

  Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

  For example, 

Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

  

The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. 
Thanks Marcos for contributing this image!

  

  一开始想了很多扯淡的方法,其中比较可行的一种是向右遍历,检查到新高度时向右扫描这层水的体积(寻找一个大于等于这一高度的,姑且叫做“墙”吧)。

  不过这种方法对于一枝独秀的高墙,对,没错,比如上面的height[8],尤其是当它是height[0]的时候,需要额外讨论很多东西。于是懒癌患者被直接劝退了。

  所以修改了一下这个方法的思路,现在我们使用两个指针l(左指针)和r(右指针)来向中间扫描。

  注意,直接计算水的体积十分麻烦,所以我们用一种简化的思路:

    用block记录总的方块数,这只需要一次扫描。

    用water记录不考虑方块占空间的水体积,这可以在接下来的扫描中计算出来。

    最后,我们返回water - block,也就是水的真实体积。

  我们看到,不考虑方块占空间的话,水的体积呈现出一个极好的(或者两个)实心阶梯”形状“。这使我们很方便的计算它的量。

  我们用lh表示已知的左侧墙高,用rh表示已知的右侧墙高,用wh表示目前水平面高度。初始状态下,它们的值都是0。

  当lh <= rh的时候,我们试图向右扫描一堵更高的左墙来装下更多的水。我们将lh的值更新为扫描到的第一堵更高的左墙。将这个过程中增加的水量加入water:

water += (r - l + 1)*(min(lh, rh) - wh)

  注意,这些水是“一层一层”地“叠放”起来的。

  然后更新水平面高度。

wh = min(lh, rh)

  当lh > rh时,做类似向左扫描加水操作。

  l,r指针交错时,返回结果。

  这个算法复杂度为O(N)。

  以下是submission。

 1 class Solution:
 2     def trap(self, height):
 3         l = 0
 4         r = len(height) - 1
 5         lh = 0
 6         rh = 0
 7         wh = 0
 8         water = 0
 9         block = 0
10         for i in height:
11             block += i
12         while l < r:
13             if lh <= rh:
14                 while l < r and height[l] <= lh:
15                     l += 1
16                 lh = height[l]
17                 water += (r - l + 1)*(min(lh, rh) - wh)
18                 wh = min(lh, rh)
19             else:
20                 while l < r and height[r] <= rh:
21                     r -= 1
22                 rh = height[r]
23                 water += (r - l + 1)*(min(lh, rh) - wh)
24                 wh = min(lh, rh)
25         return max(water - block, 0)

  另附“竖放”水层的算法,用C++实现。作者mcrystal。

class Solution {
public:
    int trap(int A[], int n) {
        int left=0; int right=n-1;
        int res=0;
        int maxleft=0, maxright=0;
        while(left<=right){
            if(A[left]<=A[right]){
                if(A[left]>=maxleft) maxleft=A[left];
                else res+=maxleft-A[left];
                left++;
            }
            else{
                if(A[right]>=maxright) maxright= A[right];
                else res+=maxright-A[right];
                right--;
            }
        }
        return res;
    }
};

 

posted @ 2017-11-02 21:45  那不勒斯冰淇淋  阅读(167)  评论(0编辑  收藏  举报