问题: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
.
思路:
解法一:
以某一个柱子为例,其上面能否蓄水,取决于其左侧柱子最大高度、右侧柱子最大高度的较小值与该柱子高度的差值。如果差值是正的,则其上方可以蓄水;否则不可以。于是问题就转换为,如何快速求得一个柱子左右两侧的最大高度。
借助两个辅助数组来完成吧。数组LM,其元素LM[i]记录0~i-1号元素中的最大值。数组RM,其元素RM[i]记录i+1~n-1号元素中的最大值。这两个数组只要左右各遍历一遍即可求得。
该方法的时间复杂度O(N),空间复杂度O(N)。
class Solution {
public:
int min(int a, int b)
{
return a>b?b:a;
}
int trap(int A[], int n) {
if(n <= 1)
return 0;
int water = 0;
int *LM = new int[n];
int *RM = new int[n];
//LM记录每个柱子左侧的最大高度
LM[0] = A[0];
for(int i=1;i<n;i++)
if(A[i] > LM[i-1])
LM[i] = A[i];
else
LM[i] = LM[i-1];
//RM记录每个柱子右侧的最大高度
RM[n-1] = A[n-1];
for(int i=n-2;i>=0;i--)
if(A[i] > RM[i+1])
RM[i] = A[i];
else
RM[i] = RM[i+1];
//计算每个柱子上面能存放的水量
for(int i=1;i<n-1;i++)
{
int k = min(LM[i], RM[i]);
if(k > A[i])
water += k - A[i];
}
delete []LM;
delete []RM;
return water;
}
};
解法二:
避免使用辅助空间,先找到数组中的最高点。该点将数组分为左右两半。对于左半侧,从左向右处理;对于右半侧,从右向左处理。处理的时候只需考虑已扫描一侧的高度即可,因另一侧有数组最高点,无需担心蓄水问题。
该方法的时间复杂度O(N),空间复杂度O(1)。
class Solution {
public:
int trap(int A[], int n) {
int max = 0;
for(int i=1;i<n;i++)
if(A[i] > A[max])
max = i;
int water = 0;
for(int i=1, top=0;i<max;i++)
if(A[i] > A[top])
top = i;
else
water += A[top] - A[i];
for(int i=n-2, top=n-1;i>max;i--)
if(A[i] > A[top])
top = i;
else
water += A[top] - A[i];
return water;
}
};
这道题让我想到了另一道题,《寻找直方图中的最大矩形》。那道题的用栈来维护一个有序序列的方法可以借鉴过来。
顺序取数组元素,同时维护一个栈(存的是下标),该栈只保存降序:
当栈为空时,可以直接将数组元素i加入栈中。
当栈不为空时,如果数组元素i比栈顶对应元素小,则继续压栈。
当栈不为空时,如果数组元素i比栈顶对应元素大,则要栈顶出栈,计算其蓄水能力(比较其与新栈顶和数组元素i的大小关系)。
该方法的时间复杂度O(N),空间复杂度O(N)。
class Solution {
public:
int min(int a, int b)
{
return a>b?b:a;
}
int trap(int A[], int n) {
if(n <= 1)
return 0;
int water = 0;
stack<int> s;
for(int i=0;i<n;i++)
{
if(s.empty())
s.push(i);
else
{
if(A[i] <= A[s.top()]) // 若小于栈顶,则入栈
s.push(i);
else
{
while(!s.empty() && A[i] > A[s.top()]) //若大于栈顶,则清算
{
int k = s.top();
s.pop();
if(!s.empty())
water += (min(A[s.top()],A[i]) - A[k])*(i-s.top()-1);
}
s.push(i);
}
}
}
return water;
}
};
注意解法二中,计算一次蓄水量时不一定就是某柱子上方所有的水量。而只是这一层次的。