携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
题目
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2
输入:height = [4,2,0,3,2,5]
输出:9
提示
- n == height.length
- 1 <= n <= 2 * 10^4
- 0 <= height[i] <= 10^5
题解
思路
首先考虑暴力做法,找找思路,暴力做法可以遍历数组,在每个位置分别往两边寻找左柱子中的最大高度和右柱子中的最大高度,找到之后,用左右最大高度的较小者减去当前柱子的高度,就是当前位置能接的水量。该方法要循环整个数组,并且每个位置要遍历数组寻找左右柱子高度的最大值,嵌套了一层循环,所以复杂度是 O(n^2)。
我们怎样加速嵌套的这层循环呢,其实可以预先计算从左往右和从右往左的最大高度数组,在循环数组的时候,可以直接拿到该位置左右两边的最大高度,当前位置的接水量就是左右两边高度的较小者减去当前位置柱子的高度
复杂度:时间复杂度 O(n),寻找左右的最大高度,循环计算每个位置的接水量,总共 3 个循环,但他们不是嵌套关系。空间复杂度是 O(n),n 是 heights 数组,用到了 leftMax 和 rightMax 数组,即存放左右两边最大高度的数组。
代码
var trap = function(height) {
const n = height.length;
if (n == 0) {//极端情况
return 0;
}
const leftMax = new Array(n).fill(0);//初始化从左往右看的最大值数组
leftMax[0] = height[0];
for (let i = 1; i < n; ++i) {
leftMax[i] = Math.max(leftMax[i - 1], height[i]);
}
const rightMax = new Array(n).fill(0);//初始化从右往左看的最大值数组
rightMax[n - 1] = height[n - 1];
for (let i = n - 2; i >= 0; --i) {
rightMax[i] = Math.max(rightMax[i + 1], height[i]);
}
let ans = 0;
//循环数组,每个位置能接的雨水量就是这个位置左右最大值的较小者减去当前的高度
for (let i = 0; i < n; ++i) {
ans += Math.min(leftMax[i], rightMax[i]) - height[i];
}
return ans;
};
结语
业精于勤,荒于嬉;行成于思,毁于随。