发现规律:如果数组两端恰好是“杯壁”,即数组两端的值是第一和第二大的,那么
雨水的面积 = 以数组长度为底且以最长杯壁为高的矩形面积 - 墙壁面积 - 空白面积;
其中,
以数组长度为底且以最长杯壁为高的矩形面积 = 第一大的值 * (|第一大的值的下标 - 第二大的值的下标| + 1);
墙壁面积,即数组元素的和,可以累加计算得出,在遍历数组的时候就可以顺便累加;
空白面积 = (第一大的值 - 第二大的值) * (|第一大的值的下标 - 第二大的值的下标|)。
因此,得到一个解题思路——尽可能地找“杯壁”。
注意事项:使用Java语言,还要注意两个int类型相乘可能会溢出,导致计算结果不正确,因此一定要注意用适当的方法转换成long类型的乘法。
import java.util.*;
public class Solution {
/**
* max water
* @param arr int整型一维数组 the array
* @return long长整型
*/
public long maxWater (int[] arr) {
if (arr == null || arr.length <= 2) return 0;
//找到最大值,以最大值为边界,求两段数组的雨水量之和。
int maxIndex = findMaxIndex(arr);
long leftRes = LeftMaxWater(arr,0,maxIndex);
long rightRes = rightMaxWater(arr,maxIndex,arr.length - 1);
return leftRes + rightRes;
}
private long rightMaxWater(int[] a, int s, int e) {
if (e - s <= 1) return 0;
//反转之后,使用LeftMaxWater
int i = s, j = e;
while (i < j){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
++i;
--j;
}
return LeftMaxWater(a,s,e);
}
private long LeftMaxWater(int[] a, int s, int e) {
if (e - s <= 1) return 0;
int firstMaxIndex = s;
long res = 0;
long currTotalWall = a[s];
for (int i = s + 1; i <= e; i++) {
currTotalWall += a[i];
if (a[i] >= a[firstMaxIndex]){
long blank = (i - firstMaxIndex + 0L) * (a[i] - a[firstMaxIndex]);
res += (i - firstMaxIndex + 1L) * a[i] - blank - currTotalWall;
firstMaxIndex = i;
currTotalWall = a[i];
}
}
return res;
}
private int findMaxIndex(int[] a) {
int r = 0;
for (int i = 1; i < a.length; i++) {
if (a[r] < a[i]){
r = i;
}
}
return r;
}
}