Trapping Rain Water

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.

分析:这道题还是有难度的。首先要明确如何计算trapped water,如果一个小矩形块上可以trap water,那么它的左侧和右侧必须有比它高的矩形块,而一个矩形块trap water的多少则由左侧最高及右侧最高的矩形块中较矮的那个决定。由上面的分析,我们就可以先算出每个矩形块左侧最大值和右侧最大值,然后计算trap water的量,一个实现代码如下,时间复杂度为O(n),但需要3个pass,空间复杂度为O(n)。

 1 class Solution {
 2 public:
 3     int trap(int A[], int n) {
 4         vector<int> max_left(n,0);
 5         vector<int> max_right(n,0);
 6         int sum = 0;
 7         for(int i = 1; i < n; i++){
 8             max_left[i] = max(max_left[i-1], A[i-1]);
 9             max_right[n-1-i] = max(max_right[n-i],A[n-i]);
10         }
11         for(int i = 1; i < n-1; i++){
12             int height =  min(max_left[i],max_right[i]);
13             if(height > A[i])
14                 sum += height - A[i];
15         }
16         return sum;
17     }
18 };

 我们还可以在上面的基础上做优化,我们的确需要每个矩形块左侧的最大值和右侧的最大值,但事先计算并保存是不必要的。一个巧妙的解决办法是先找到所有矩形块的最大值,然后以该最大值为分界分成两部分,这个最大矩形块是左侧子数组的右侧最大值,是右侧子数组的左侧最大值。然后对于左侧子数组,我们只要从左到右扫描找到左侧最大值即可;对于右侧子数组,从右到左扫描找到右侧最大值。时间复杂度为O(n),但只有两个pass,空间复杂度为O(1)。代码如下:

class Solution {
public:
    int trap(int A[], int n) {
        int result = 0;
        int maxBar = 0;
        
        for(int i = 1; i < n; i++)
            if(A[i] > A[maxBar]) maxBar = i;
        //from left to right
        for(int i = 0, peak = 0; i < maxBar; i++){
            if(A[i] < peak) result += peak - A[i];
            else peak = A[i];
        }
        //from right to left
        for(int i = n-1, peak = 0; i > maxBar; i--){
            if(A[i] < peak) result += peak - A[i];
            else peak = A[i];
        }
        
        return result;
    }
};

 除上面的解法外,我们还可以采用与Largest Rectangle in Histogram中用栈保存数据的方法。在Largest Rectangle in Histogram中,栈保存的是某元素左侧的较小值,所以如果当前元素大于栈顶元素,就把当前元素压栈,否则将栈顶元素出栈直到栈顶元素小于当前元素为止。这道题中恰好与Largest Rectangle in Histogram相反,栈保存的是某元素左侧的较大值,如果当前元素小于栈顶元素,就把当前元素压栈,否则将栈顶元素出栈直到栈顶元素大于当前元素为止。当然我们要清楚,这种方法的trapped water是按横着的长条来算的,不同于第一二中方法按竖着的长条来算。代码如下:

class Solution {
public:
    int trap(int A[], int n) {
        int result = 0;
        stack<int> Q;//stores element index
        
        for(int i = 0; i < n;){
            if(Q.empty() || A[Q.top()] > A[i]){
                Q.push(i);
                i++;
            }
            else{
                int index = Q.top();
                Q.pop();
                if(!Q.empty())
                    result += (min(A[i], A[Q.top()]) - A[index]) * (i - Q.top() -1);  
            }
        }
        
        return result;
    }
};

 

posted on 2014-08-09 23:01  Ryan-Xing  阅读(124)  评论(0)    收藏  举报