LeetCode042 Trapping Rain Water

敢说敢作敢为, 无怨无恨无悔。这篇文章主要讲述LeetCode042 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.
For example, 

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

LeetCode042 Trapping Rain Water

文章图片

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. 
题解:
遍历数组,找到局部最小值,方法是如果当前值大于或等于前一个值,或者当前值大于后一个值则跳过。找到了局部最小值后,然后向左找左边的最大值,再向右找右边的最大值,找右边最大值时要注意当其大于左边最大时就停止寻找。然后算出从左边最大值到右边最大值之间能装的水量,之后从右边最大值的位置开始继续找局部最小值,以此类推直到遍历完整个数组。(from  here)
Solution 1()
class Solution { public: int trap(vector< int> & height) { int result = 0, n = height.size(), left = 1, right = 0; for(int i=1; i< n-1; i++) { if(height[i] > = height[i-1] || height[i] > height[i+1]) continue; for(left = i-1; left> 0; --left) { if(height[left]> =height[left-1]) break; } right = i + 1; for(int j=i+1; j< n; ++j) { if(height[j]> =height[right]) { right = j; if(height[right]> =height[left]) break; } } int h = min(height[left],height[right]); for(int k=left+1; k< right; ++k) { if(h> height[k]) result += h - height[k]; } i = right; } return result; } };

 
对于每一个值,其与另外两个值组成的容器收集的雨水最大量肯定是在其左右两边的最大值作为容器的两个壁的情况下获得的,具体在题目里就是这两个最大值的较小值与当前值的差(当这个最小值大于当前值时,否则收集不到雨水)。使用动态规划,初始化一维数组vector< int> dp(n,0)。然后遍历两遍数组,第一次遍历从左边扫描找出当前位置左边的最大值,并存放到dp中;第二次遍历从右边扫描找出当前位置右边的最大值,然后与dp中保存的左边最大值比较,存下二者之中的较小值,并且将这个值与当前值比较,如果大于当前值,则收集的雨水总量应该加上这个较小值与当前值的差值。用一个数组 left[i] 表示第 i 根柱子左边最高的柱子的高度(from here)
用一个数组 right[i] 表示第 i 根柱子右边最高的柱子的高度
1.从左到右扫描,left[i] = max(left[i - 1], A[i - 1])
2.从右到左扫描,right[i] = max(right[i + 1], A[i + 1])
3.第 i 根柱子上能储蓄的水为 min(left[i], right[i]) - A[i],因为这时 left[i] 已经没用了,可以用它来存放这个值。(from here)
复杂度:时间O(n), 空间O(n)
Solution 2 ()
class Solution { public: int trap(vector< int> & height) { int result = 0, lmax = 0, rmax = 0, n = height.size(); vector< int> dp(n,0); for(int i=0; i< n; i++) { dp[i] = lmax; lmax = max(lmax, height[i]); } for(int i=n-1; i> =0; i--) { dp[i] = min(dp[i], rmax); rmax = max(rmax, height[i]); if(dp[i] > height[i]) { result += dp[i] - height[i]; } } return result; } };

根据上面的雨水收集的分析,任意3个值组成的雨水收集量即是两边的较小值与中间值的差值(如果前者大于后者)。因此可以设置两个指针从两头往中间遍历,这两个指针所指的值即为容器的两个壁。低的壁(较小值)决定了收集雨水的量,因此中间值设为从较小值的后续值,并且算出收集的雨水,直到遇到一个值大于这个较小值,并且更新较小值为当前值。这样只需一趟扫描就可以解决问题。(from  here)
Solution 3 ()
class Solution { public: int trap(vector< int> & height) { int result = 0, left = 0, right = height.size() - 1; while (left < right) { int h = min(height[left], height[right]); if (h == height[left]) { ++left; while (left < right & & height[left] < h) { result += h - height[left++]; } } else { --right; while (left < right & & height[right] < h) { result += h - height[right--]; } } } return result; } };

  The idea is:
1) find the highest bar.
2) traverse the bar from left the highest bar.becasue we have the highest bar in right, so, any bar higher than its right bar(s) can contain the water.
3) traverse the bar from right the highest bar.becasue we have the highest bar in left, so, any bar higher than its left bar(s) can contain the water.  (from here  ChenHao)
Solution 4 ()
class Solution { public: int trap(int a[], int n) { int result = 0; //find the highest value/position int maxHigh = 0; int maxIdx = 0; for(int i=0; i< n; i++){ if (a[i] > maxHigh){ maxHigh = a[i]; maxIdx = i; } } //from the left to the highest postion int prevHigh = 0; for(int i=0; i< maxIdx; i++){ if(a[i] > prevHigh){ prevHigh = a[i]; } result += (prevHigh - a[i]); } //from the right to the highest postion prevHigh=0; for(int i=n-1; i> maxIdx; i--){ if(a[i] > prevHigh){ prevHigh = a[i]; } result += (prevHigh - a[i]); }return result; } };

【LeetCode042 Trapping Rain Water】 






    推荐阅读