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!
Subscribe to see which companies asked this question
解法1:遍历数组,找到局部最小值,方法是如果当前值大于或等于前一个值,或者当前值大于后一个值则跳过。找到了局部最小值后,然后向左找左边的最大值,再向右找右边的最大值,找右边最大值时要注意当其大于左边最大时就停止寻找。然后算出从左边最大值到右边最大值之间能装的水量,之后从右边最大值的位置开始继续找局部最小值,以此类推直到遍历完整个数组。
class Solution { public: int trap(vector<int>& height) { int sumTrap = 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]) sumTrap += h - height[k]; } i = right; } return sumTrap; } };
解法2:对于每一个值,其与另外两个值组成的容器收集的雨水最大量肯定是在其左右两边的最大值作为容器的两个壁的情况下获得的,具体在题目里就是这两个最大值的较小值与当前值的差(当这个最小值大于当前值时,否则收集不到雨水)。使用动态规划,初始化一维数组vector<int> dp(n,0)。然后遍历两遍数组,第一次遍历从左边扫描找出当前位置左边的最大值,并存放到dp中;第二次遍历从右边扫描找出当前位置右边的最大值,然后与dp中保存的左边最大值比较,存下二者之中的较小值,并且将这个值与当前值比较,如果大于当前值,则收集的雨水总量应该加上这个较小值与当前值的差值。
class Solution { public: int trap(vector<int>& height) { int n = height.size(), left_max = 0, right_max = 0, sum_trap = 0; vector<int> dp(n, 0); for (int i = 0; i < n; ++i) { dp[i] = left_max; left_max = max(left_max, height[i]); } for (int i = n - 1; i >= 0; --i) { dp[i] = min(dp[i], right_max); right_max = max(right_max, height[i]); if (dp[i] > height[i]) sum_trap += dp[i] - height[i]; } return sum_trap; } };
解法3:根据上面的雨水收集的分析,任意3个值组成的雨水收集量即是两边的较小值与中间值的差值(如果前者大于后者)。因此可以设置两个指针从两头往中间遍历,这两个指针所指的值即为容器的两个壁。低的壁(较小值)决定了收集雨水的量,因此中间值设为从较小值的后续值,并且算出收集的雨水,直到遇到一个值大于这个较小值,并且更新较小值为当前值。这样只需一趟扫描就可以解决问题。
class Solution { public: int trap(vector<int>& height) { int n = height.size(), left = 0, right = n - 1, sum_trap = 0; while (left < right) { int min_value = min(height[left], height[right]); if (height[left] == min_value) { ++left; while (height[left] < min_value) sum_trap += min_value - height[left++]; } else { --right; while (height[right] < min_value) sum_trap += min_value - height[right--]; } } return sum_trap; } };