前言
本人太菜,只想出了暴力解法:逐行计算,只要遇到 [1, 0..., 1] 这种模式就累积中间 0 的个数。吭哧吭哧写出来之后发现超时了……本来还想等AC之后大喊一句:“暴力可耻但有用!”,最后还是向大佬们的高端题解屈服了:P
思路
逐列计算,对于每一列,寻找左边的最高值 max_left 和右边的最高值 max_right:
求 min_height = Math.max(max_left, max_right),根据木桶效应,当前列最多储水min_height - height[i]
max_left 和 max_right 可以通过动态规划的方式计算。
题解
var trap = function (height) {
let len = height.length;
if (len < 2) {
return 0;
}
let left = [];
left[0] = 0; // 初始化边界值
let right = [];
right[len - 1] = 0;
let sum = 0;
for (let i = 1; i < len - 1; i++) {
// 存储每一列左边数的最大值
left[i] = Math.max(left[i - 1], height[i - 1]);
}
for (let j = len - 2; j >= 1; j--) {
// 存储每一列右边数的最大值
// 因为要更新右边最大值,需要一点一点加大右边的范围
// 所有从右至左更新
right[j] = Math.max(right[j + 1], height[j + 1]);
}
for (let n = 1; n < len - 1; n++) {
// 累加每一列的雨水
min_height = Math.min(left[n], right[n]);
if (height[n] < min_height) {
sum += min_height - height[n];
}
}
return sum;
};
双指针改良版
两个指针分别从左边和右边出发,更新max值。哪边的max值小,说明对于当前指针上的值,min(left[n], right[n])已经确定,
cr: leetcode-cn.com/problems/tr…
leftMax > rightMax,这个 rightMax 就是我们要的值。
左区间未遍历到的值如果比 leftMax 小怎么办?
不用管,因为我们只要区间里的最大值;
如果比 leftMax 大怎么办?
不用管,因为我们只关心 max 之间的最小值
var trap = function (height) {
let waterSum = 0;
const N = height.length;
let leftMax = height[0];
let L = 1;
let rightMax = height[N - 1];
let R = N - 1;
while (L <= R) {
if (leftMax <= rightMax) {
waterSum += Math.max(0, leftMax - height[L]);
leftMax = Math.max(leftMax, height[L++]);
} else {
waterSum += Math.max(0, rightMax - height[R]);
rightMax = Math.max(rightMax, height[R--]);
}
}
return waterSum;
//时间复杂度O(n)
//空间复杂度O(1)
};