题目
思路
- 首先我们要明确如何计算每条柱子的接水量:
- 每条柱子对应接到的雨水量=该柱子左边最大值和右边最大值中的较小值-该柱子本身的高度。
- 举例:第二条柱子自身高度为0,左边最大值为1,右边最大值为3,取较小值1-自身高度0=接水量1
- 该题暴力解法思路:第一层遍历每根柱子,第二层需要分别遍历该柱子的左边和右边,找出最大值,再计算该柱子的接水量。本文采用双指针法,可以在暴力解法基础上优化复杂度,以下为双指针法思路讲解。
- 根据计算方法可知,我们计算每根柱子接水量都要找左右两边的最大值,所以我们可以将每根柱子对应的左右两边最大值先计算出来分别存在两个数组中,计算每根柱子接水量时可以直接使用,不用再分别去做左右两边最大值的比较,以下是如何拿到左右最大值数组思路:
- 左边
- 初始化一个与高度数组
height
长度相同的数组leftMax
,用于存储每个位置左边的最大值。 - 从数组的第一个元素(索引为 0)开始遍历高度数组
height
。 - 对于第一个元素,它左边没有其他元素,所以
leftMax[0] = height[0]
。 - 对于后续的元素(索引
i > 0
),leftMax[i]
等于height[i]
和leftMax[i - 1]
中的较大值,即leftMax[i] = Math.max(height[i], leftMax[i - 1])
。通过这种方式,在遍历过程中不断更新每个位置左边的最大值。
- 初始化一个与高度数组
- 右边的计算方式和左边相似,只是反向比较而已。
- 左边
- 在得到左右两边最大值数组后,就可以遍历题目给出的高度数组,根据前面的计算方式算出每个柱子接水量再逐一相加,得到总雨水量。
代码示例
// 使用双指针法解决接雨水问题
var trap = function (height) {
// 获取高度数组的长度
const len = height.length;
// 如果数组长度小于等于2,无法形成可以接水的区域,直接返回0
if (len <= 2) return 0;
// 创建一个长度为len的数组maxLeft,初始值都为0,用于记录每个柱子左边的最大高度
const maxLeft = new Array(len).fill(0);
// 创建一个长度为len的数组maxRight,初始值都为0,用于记录每个柱子右边的最大高度
const maxRight = new Array(len).fill(0);
// 初始化maxLeft数组的第一个元素,即第一个柱子左边的最大高度就是它自身的高度
maxLeft[0] = height[0];
// 遍历从第二个柱子(索引为1)到最后一个柱子(索引为len - 1)
for (let i = 1; i < len; i++) {
// 更新当前柱子左边的最大高度,取当前柱子高度和它左边已记录的最大高度中的较大值
maxLeft[i] = Math.max(height[i], maxLeft[i - 1]);
}
// 初始化maxRight数组的最后一个元素,即最后一个柱子右边的最大高度就是它自身的高度
maxRight[len - 1] = height[len - 1];
// 从倒数第二个柱子(索引为len - 2)开始向前遍历到第一个柱子(索引为0)
for (let i = len - 2; i >= 0; i--) {
// 更新当前柱子右边的最大高度,取当前柱子高度和它右边已记录的最大高度中的较大值
maxRight[i] = Math.max(height[i], maxRight[i + 1]);
}
// 用于记录可以接住的雨水总量
let sum = 0;
// 遍历每个柱子(从索引0到len - 1)
for (let i = 0; i < len; i++) {
// 计算当前柱子可以接住的雨水量,等于它左右两边最大高度的较小值减去自身高度
let count = Math.min(maxLeft[i], maxRight[i]) - height[i];
// 如果计算出的雨水量大于0,说明当前柱子可以接住雨水,将其累加到总量sum中
if (count > 0) sum += count;
}
// 返回最终可以接住的雨水总量
return sum;
};
欢迎指正!