对于array里的每一个位置来说,它的积水量 = min(左边最高的墙, 右边最高的墙) — 自己位置的高度。[哭了,我连这个都不知道,感觉输在起跑线上了]
总结,这道题其实就是寻找每个位置的
基准:min(左边最高的墙, 右边最高的墙) <—— 重点在于如何寻找基准 [min of two side max]
自高:自己位置的高度
Brute Force:
基准:遍历左侧找到左边最高墙,遍历右侧找到右边最高墙
遍历每一个位置,在每一个位置,用上面的方法寻找基准,并计算积水量。最后将所有的积水量加起来即可。
有什么办法可以简化吗?
计算2左侧的最高墙,需要对比0,1的高度
计算3左侧的最高墙,需要对比0,1,2的高度
[我听到了DP的召唤]
解法一 DP
基准:基于左侧一位的最高左墙找到当前最高左墙,基于右侧一位的最高右墙找到当前最高右墙。
第一次从左往右遍历,存入每位左侧最高的数;第二次从右往左遍历,先得到右测最高的数,并与之前的比较得到最小值,最后与自己位数上的数比较,累计到最终答案上。
Use Two Arrays
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size();
if(len<3) return 0;
vector<int> leftH(len, 0);
vector<int> rightH(len, 0);
int res=0, mx = 0;
for(int i=0;i<len;i++){
leftH[i] = mx;
mx = max(mx, height[i]);
}
mx = 0;
for(int i=len-1;i>=0;i--){
rightH[i] = mx;
mx = max(mx, height[i]);
if(min(rightH[i], leftH[i])>height[i]){
res += min(rightH[i], leftH[i])-height[i];
}
}
return res;
}
};
Common Mistake
Correct: int res=0, mx = 0;
Wrong: int res, mx = 0;
Use One Array
每个位置上的右侧最高值不会再用到了,所以不需要用另一个array rightH来存。
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size();
if(len<3) return 0;
vector<int> leftH(len, 0);
int res=0, mx = 0;
for(int i=0;i<len;i++){
leftH[i] = mx;
mx = max(mx, height[i]);
}
mx = 0;
for(int i=len-1;i>=0;i--){
if(min(mx, leftH[i])>height[i]){
res += min(mx, leftH[i])-height[i];
}
mx = max(mx, height[i]);
}
return res;
}
};
不用mx
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size();
if(len<3) return 0;
vector<int> leftH(len, 0);
int res=0;
leftH[0] = height[0];
for(int i=1;i<len-1;i++){
leftH[i] = max(height[i], leftH[i-1]);
}
leftH[len-1] = height[len-1];
for(int i=len-2;i>0;i--){
int rightH = max(height[i], leftH[i+1]);
res += min(rightH, leftH[i])-height[i];
leftH[i] = rightH;
}
return res;
}
};
Back to the Beginning: 遍历每一个位置,在每一个位置,找到左边最高墙和右边最高墙,并计算积水量。我们真的需要左边最高墙和右边最高墙吗?我们真正需要的是两者的最小值。
What! How these two are different?
左边最高墙和右边最高墙:需要遍历当前位置的所有左侧墙和右侧墙
两者的最小值:知道一侧的最高墙,并且知道此墙低于另一侧的一面墙,那么这一侧的最高墙就是我们寻找的两者最小值。举个例子,height[0]<height[len-1],那么我们就知道对于height[1]来说,height[0]就是左侧最高墙,右侧最高墙并不重要的了,因为肯定高于height[0],所以position 1形成的坑就是height[0]-height[1] if height[0]>height[1]。
总结一下,我们到底需要什么?
基准:遍历一侧得到最高墙,并且知道另一侧的一面墙高于此墙。[哈哈,two pointers 两边向中间遍历~]
解法二 Two Pointers
Continue with the above example:
if height[0]>height[1]: height[0] will still be [the max of the left side] and [the min of the max left and max right]
if height[0]<height[1]: height[1] will replace height[0] to be [the max of the left side] and [the min of the max left and max right] requires an additional comparison.
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size();
if(len<3) return 0;
int l=0, r=len-1, res=0;
while(l<r){
int mn = min(height[l], height[r]);
if(mn==height[l]){
l++;
while(l<r && mn>height[l]){
res += mn-height[l++];
}
}else{
r--;
while(l<r && mn>height[r]){
res += mn-height[r--];
}
}
}
return res;
}
};
优化
基准:取两侧较小的一面墙,如果大于基准,update。
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size();
if(len<3) return 0;
int l=0, r=len-1, res=0, level=0;
while(l<r){
int curH = height[height[l] < height[r] ? l++: r--];
level = max(level, curH);
res += level - curH;
}
return res;
}
};
解法三 Stack
之前我们都是每一位每一位竖向思考,那么现在我们来尝试一下横向思考。
水坑是怎么形成的?左半边会有一个下降的趋势,然后当右半边升高时,每升高一层,就会形成一层的水坑。
总结,每次算一个大水坑的大小。运用stack存左半边所有墙,遍历右半边所有墙的时候一层层算出坑的大小。
class Solution {
public:
int trap(vector<int>& height) {
int res =0, i=0, n=height.size();
stack<int> st;
while(i<n){
if(st.empty() || height[i]<=height[st.top()]){
st.push(i++);
}else{
int t=st.top(); st.pop();
if(st.empty()) continue;
res += (min(height[i], height[st.top()])-height[t])*(i-st.top()-1);
}
}
return res;
}
};
Reference:http://www.cnblogs.com/grandyang/p/4402392.html