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.
2015年阿里校招的笔试有这一题,是原题,要求只一遍遍历完成。首先看一下一次遍历怎么做。
这里用到一个小技巧,使用两个指针分别从头从尾向中间进行遍历。迭代开始首先将头尾指针分布至于首尾处,我们需要比较两处的墙高,找到比较低的一边,将其向前继续逐步推进,如果遇到了更低的,那么说明此处必有积水,现在就是思考一下这个水池停止在哪里,其实很简单,如果遇到一个更高的(都是和外层迭代中比较低的那处比较)那么说明这个水池结束,回到外层迭代,直到指针相遇。
代码如下:
class Solution {
public:
int trap(vector<int>& height) {
int len=height.size();
if(len<=2){
return 0;
}
int res=0;
int l=0;
int r=len-1;
while(l<r){
int mini=min(height[l],height[r]);
if(height[l]==mini){
l++;
while(l<r&&height[l]<mini){
res+=mini-height[l];
l++;
}
}
else{
r--;
while(l<r&&height[r]<mini){
res+=mini-height[r];
r--;
}
}
}
return res;
}
};
如果不限制只遍历一次,我们用两次遍历找到每个墙上积水量,然后累加即可。怎么计算每个墙上的积水?这个问题搞清了这个题目就算是明白了,不然使用其他更复杂的方法可能是没有找到问题的实质(比如以水池做为计算单位,这样做会导致解法非常复杂)。每个墙的左边和右边最高的墙才能决定这个墙上的水量,其中水平线由较低的决定,假如水平线低于这个墙,说明这个墙上不可能有积水。剩下的就是简单的寻找最大值最小值的编程工作了。
class Solution {
public:
int trap(vector<int>& height) {
int len=height.size();
if(len<=2){
return 0;
}
int res=0;
int container[len-1];
int mx=0;
for(int i=0;i<len;++i){
container[i]=mx;
mx=max(container[i],height[i]);
}
mx=0;
for(int i=len-1;i>=0;--i){
container[i]=min(mx,container[i]);
mx=max(height[i],mx);
res+=height[i]-container[i]<0?container[i]-height[i]:0;
}
return res;
}
};
使用双指针产生了一些方便的解法,比如判断链表有无环的快慢指针,比如链表删除重复元素的前后指针,以及这里用到前后区域指针。他们的共同点在于指针的位置或者所指元素值的前后或者大小关系中蕴含了解的结构,这里需要我们仔细体会。