题目
给定一个数组arr,已知其中所有的值都是非负的,将这个数组看作一个容器,请返回容器最多能装多少水,比如,arr = [3,1,2,5,2,4],根据值画出約直方图就是容器形状,该容器可以装下5格水,再比如,arr = [4,5,1,3,2],该容器可以装下2格水
- 题意为能装下的雨水为蓝色区域的5格
- i 列能装下的水为 i 左边最高的高度,以及 i 右边最高的高度,最小的那个与 i 位置高度的差值 m,如果 m > 0,返回 m,如果 m < 0,说明 i 位置比一方高,水会流出,返回 0
- 所以核心问题变成了求 i 位置左右两边的最大值,采用双指针(left、rigth)的做法,指针代表已经遍历区域的边界,在区域中通过两个变量保存左右区域的最大值
- left、right 从首尾开始,最值就是首尾,i从 1 开始,假设左边比右边小,那么 arr[i] 能够求解了,因为此时 i 位置能够求出两边区域最大值中最小的就在左边
- left++,如果i位置的值比左边区域的最值大,那么更新左边区域最值,继续比较两边最值,哪边小就求解哪边,然后指针移动
- 因为从首尾开始,每个位置左右区域能够由小到大依次求得最值,因为指针不会回退,所以时间复杂度很低
function process(arr) {
let left = 1,
right = arr.length - 2,
leftMax = 0,
rightMax = 0,
all = 0;
// 只有 1 列或 2 列装不了雨水
if (arr.length <= 2) {
return 0;
}
while (left <= right) {
if (leftMax < rightMax) {
const count = Math.max(Math.min(leftMax, rightMax) - arr[left], 0);
if (count > max) {
all += count;
}
left++;
if (arr[left] > leftMax) {
leftMax = arr[left];
}
} else {
const count = Math.max(Math.min(leftMax, rightMax) - arr[right], 0);
if (count > max) {
all += count;
}
right--;
if (arr[right] > rightMax) {
rightMax = arr[right];
}
}
}
return all;
}