您的位置:首页 > 移动开发

[LeetCode] Trapping Rain Water 收集雨水

2015-04-08 14:46 225 查看
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!

这道收集雨水的题跟之前的那道 Largest Rectangle in Histogram 直方图中最大的矩形 有些类似,但是又不太一样,我最先想到的方法有些复杂,但是也能通过OJ,想法是遍历数组,找到局部最小值,方法是如果当前值大于或等于前一个值,或者当前值大于后一个值则跳过,找到了局部最小值后,然后我们首先向左找到左边的最大值,再找右边的最大值,找右边最大值时要注意当其大于左边最大时时就停止寻找了,然后算出从左边最大值到右边最大值之间能装的水量,之后从右边最大值的位置开始继续找局部最小值,以此类推直到遍历完整个数组,代码如下:

解法一:

class Solution {
public:
int trap(int A[], int n) {
int res = 0, left = 0, right = 0, level = 0;
for (int i = 1; i < n - 1; ++i) {
if (A[i] >= A[i - 1] || A[i] > A[i + 1]) continue;
for (left = i - 1; left > 0; --left) {
if (A[left] >= A[left - 1]) break;
}
right = i + 1;
for (int j = i + 1; j < n; ++j) {
if (A[j] >= A[right]) {
right = j;
if (A[right] >= A[left]) break;
}
}
level = min(A[left], A[right]);
for (int j = left + 1; j < right; ++j) {
if (level - A[j] > 0) res += (level - A[j]);
}
i = right;
}
return res;
}
};


那么我们再来看另一种方法,这种方法是基于动态规划Dynamic Programming的,我们维护一个一维的dp数组,这个DP算法需要遍历两遍数组,第一遍遍历dp[i]中存入i位置左边的最大值,然后开始第二遍遍历数组,第二次遍历时找右边最大值,然后和左边最大值比较取其中的较小值,然后跟当前值A[i]相比,如果大于当前值,则将差值存入结果,代码如下:

解法二

// DP
class Solution {
public:
int trap(int A[], int n) {
int res = 0, mx = 0;
vector<int> dp(n, 0);
for (int i = 0; i < n; ++i) {
dp[i] = mx;
mx = max(mx, A[i]);
}
mx = 0;
for (int i = n - 1; i >= 0; --i) {
dp[i] = min(dp[i], mx);
mx = max(mx, A[i]);
if (dp[i] - A[i] > 0) res += (dp[i] - A[i]);
}
return res;
}
};


最后我们来看一种只需要遍历一次即可的解法,这个算法需要left和right两个指针分别指向数组的首尾位置,从两边向中间扫描,在当前两指针确定的范围内,先比较两头找出较小值,如果较小值是left指向的值,则从左向右扫描,如果较小值是right指向的值,则从右向左扫描,若遇到的值比当较小值小,则将差值存入结果,如遇到的值大,则重新确定新的窗口范围,以此类推直至left和right指针重合,具体参见代码如下:

解法三

// One Pass
class Solution {
public:
int trap(int A[], int n) {
int res = 0, left = 0, right = n - 1;
while (left < right) {
int mn = min(A[left], A[right]);
if (A[left] == mn) {
++left;
while (left < right && A[left] < mn) {
res += (mn - A[left]);
++left;
}
} else {
--right;
while (left < right && A[right] < mn) {
res += (mn - A[right]);
--right;
}
}
}
return res;
}
};


LeetCode All in One 题目讲解汇总(持续更新中...)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: