https://oj.leetcode.com/problems/trapping-rain-water/
具体描述请进链接看图,里面有图....有图...
public int trap(int[] A)
问题分析:这题有两种解法。第一种是时间ON,空间ON的。这种解法利用到了本题的第一个特性:当前柱子的水量取决于它左边所有柱子最高的那根和右边所有柱子最高的那根的短者,然后这个短者和当前柱子的高度差,就是当前柱子上的水量。所以我们需要用一个长度为N的数组记录其中一边(这里是左边)的当前最大值,然后反向扫,用一个值维护另一边的当前最大值,再和当前的柱子比较取值。代码如下:
public int trap(int[] A) {
if(A == null || A.length < 2)
return 0;
//Sol 1. O(N) time O(N) space
int[] leftmost = new int[A.length];
int res = 0;
leftmost[0] = A[0];
for(int i = 1; i < A.length; i++){
leftmost[i] = Math.max(A[i], leftmost[i - 1]);
}
int cur_max = A[A.length - 1];
for(int i = A.length - 2; i >= 0; i--){
cur_max = Math.max(A[i], cur_max);
int cur_bot = Math.min(leftmost[i], cur_max);
res += cur_bot > A[i] ? cur_bot - A[i] : 0;
}
return res;
}
第二种则是时间ON,空间O1的。同样,这种算法是基于上面的思想而成立,但是它还加入了贪心法的思想。具体做法如下:
保留头尾指针往中间扫,
1.选取左右中更小者。
2.更小者不停循环往中间逼近,直到遇到一个比这个更小者更大的值停下,返回第一步。直到左右指针都走到一起
3.在2的过程里不停收集结果。
贪心法论证如下:
1.假如A[left] < A[Right] ,在第二步循环的过程里,A[left] ~ A[left + k] 的过程里,A[left]便是符合左边最大值的那个值,也就是这个A[left] 就相当于上面那个解法的leftmost,但因为这个leftmost < right, 那么我们也可以得出leftmost < right <= rightmost。 所以这个leftmost其实就是上面的Math.min(leftmost[i], cur_max)。当第二步循环被破,我们就重新寻找另一个类似的leftmost或者rightmost,所以在第二步循环的过程里,A[left] ~ A[left + k]的求值过程其实就符合了上面我们提到的那个特性。
给出代码如下:
public int trap(int[] A) {
if(A == null || A.length < 2)
return 0;
//Sol 2. O(n) time O(1) space
int left = 0, right = A.length - 1;
int res = 0;
while(left < right){
int min = Math.min(A[left], A[right]);
if(A[left] == min){
left++;
while(left < right && A[left] < min){
res += min - A[left];
left++;
}
}else{
right--;
while(left < right && A[right] < min){
res += min - A[right];
right--;
}
}
}
return res;
}
2018-09-16 Updated:
看了一下O(1)的解法,总觉得有更简化之策。原理是跟上面描述的贪心法是一样的,但是代码更容易理解和简化,给出代码如下:
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int result = 0;
int leftMax = 0, rightMax = 0;
while (left <= right) {
leftMax = Math.max(height[left], leftMax);
rightMax = Math.max(height[right], rightMax);
if (leftMax < rightMax) {
result += Math.max(leftMax - height[left], 0);
left++;
} else {
result += Math.max(rightMax - height[right], 0);
right--;
}
}
return result;
}