容器盛水问题
容器盛水问题
给定一个整形数组arr,已知其中所有的值都是非负的,将这个数组看作一个容器,请返回容器能装多少水。
解析
对于每一个位置i,它上方所能容纳的水的容量等于:
Max{Min{i位置左侧的最大值,i位置右侧的最大值}-arr[i],0}
而整个容器所能容纳的水的容量就为每一个位置上所能容纳的水的容量之和,第一个位置和最后一个位置上方所能容纳的水的容量为0。
为此,要求两个辅助数组leftMax和rightMax。leftMax[i]表示arr[0..i-1]中的最大值,rightMax[i]表示arr[i+1,arr.length-1]中的最大值。
public long maxWater (int[] arr){
if (arr == null || arr.length <= 2){
return 0;
}
int n = arr.length;
// leftMax[i]表示arr[i]左侧的最大值
// rightMax[i]表示arr[i]右侧的最大值
long[] leftMax = new long[n];
long[] rightMax = new long[n];
long left = arr[0];
long right = arr[n-1];
for (int i = 1; i < n; i++) {
left = left >= arr[i-1] ? left : arr[i-1];
leftMax[i] = left;
}
for (int i = n-2; i > 0; i--) {
right = right >= arr[i+1] ? right : arr[i+1];
rightMax[i] = right;
}
long res = 0;
// 容器盛水的容量等于,对于每一个位置i的上方的容量之和,那么每一个位置上方的水容量为:
// Max{Min{i位置左侧的最大值,i位置右侧的最大值}-arr[i],0}
for (int i = 1; i < n - 1; i++) {
res += Math.max(Math.min(leftMax[i],rightMax[i])-arr[i],0);
}
return res;
}
优化一下空间复杂度,将其由上面的O(n)变为O(1):
设定左右两个指针L和R,初始值设置为1和n-2;然后再设置两个辅助变量leftMax和rightMax,分别表示L左侧的最大值和R右侧的最大值。那么求解的过程就是:
如果leftMax<=rightMax,那么说明L位置的右侧的最大值一定不会小于rightMax,那么决定L位置处水容量大小的决定因素就是rightMax了,所以此时可以求解L位置容纳的水量:
Max{leftMax - arr[L],0}
然后更新leftMax,L向右移动:
leftMax = leftMax >= arr[L] ? leftMax : arr[L];
对于leftMax>rightMax,也是同样的道理,说明R位置左侧的最大值一定不会小于leftMax,那么决定R位置处的水容量的大小的决定因素就是leftMax了,所以此时可以求解R位置的水量:
Max(rightMax-arr[R],0)
然后更新rightMax,R向左移动:
rightMax = rightMax >= arr[R] ? rightMax : arr[R];
最终的结果就是将L和R位置上的容量相加起来的结果。
public long maxWater (int[] arr){
if (arr == null || arr.length <= 2){
return 0;
}
int n = arr.length;
long res = 0;
int leftMax = arr[0];
int rightMax = arr[n-1];
int L = 1;
int R = n - 2;
while (L <= R){
if (leftMax <= rightMax){
res += Math.max(leftMax - arr[L],0);
leftMax = leftMax >= arr[L] ? leftMax : arr[L];
L++;
}else {
res += Math.max(rightMax-arr[R],0);
rightMax = rightMax >= arr[R] ? rightMax : arr[R];
R--;
}
}
return res;
}