这道题思维很巧妙,不管是two pointer方法,还是stack方法,能想出来的都太厉害了!
首先,two pointer两遍扫描,对于每个位置来说,找到它左右最高的bar,那么当前位置能储存的水量就是两边最高bar较小那个的值-自己的值,代码如下:
public int trap(int[] A){
if(A==null ||A.length==0) return 0;
int trap = 0;
int[] lmax = new int[A.length];
int[] rmax = new int[A.length];
lmax[0]=0;
for(int i=1; i<A.length; i++){
lmax[i] = Math.max(A[i-1],lmax[i-1]);
}
rmax[A.length-1]=0;
for(int i=A.length-2; i>=0; i--){
rmax[i] = Math.max(rmax[i+1],A[i+1]);
}
for(int i=0; i<A.length; i++){
int tmp = Math.min(lmax[i],rmax[i])-A[i];
if(tmp>0) trap+=tmp;
}
return trap;
}
另外一种DP只需要一遍扫描,即一个指针从左往右扫,一个指针从右往左扫,那边值小哪边就是装水的限制,所以值小的那边继续扫,直到扫到比初始的限制值大为止,扫的过程中记录每个位置能装水的数量,直到两个指针相遇:
public int trap(int[] A){
if(A==null ||A.length==0) return 0;
int trap = 0;
int start = 0;
int end = A.length-1;
while(start<end){
if(A[start]<=A[end]){
int tmp = A[start];
while(A[start]<=tmp){
trap+= tmp-A[start];
start++;
if(start==end) return trap;
}
}
else if(A[start]>A[end]){
int tmp = A[end];
while(A[end]<=tmp){
trap+= tmp-A[end];
end--;
if(start==end) return trap;
}
}
}
return trap;
}
刚看到这道题的时候,就觉得应该用stack,但想了半天也没想出来正确的计算方法,还是看的答案,简洁的代码和解释见:
http://n00tc0d3r.blogspot.com/2013/06/trapping-rain-water.html
下面是我重写的代码:
public int trap(int[] A) {
Stack<Integer> bar = new Stack<Integer>();
int trap = 0;
for(int i=0; i<A.length; i++){
if(bar.size()==0) bar.push(i);
else{
if(A[i]<=A[bar.peek()]){
bar.push(i);
}
else{
while(bar.size()!=0 && A[bar.peek()]<A[i]){
int tmp = bar.pop();
if(bar.empty()) break;
trap += (Math.min(A[i],A[bar.peek()])-A[tmp])*(i-bar.peek()-1);
}
bar.push(i);
}
}
}
return trap;
}
没有原代码简洁,不过好歹自己按思路写一遍,更能记得住。
总结:这种和两边值有关的题都可以用两边扫描的方法,另外用stack也可以存储左边的值,等找到合适的右边值后再回到stack中看左边的值。