返回

剖析LeetCode 42:接雨水——JavaScript栈解

前端

深入剖析 LeetCode 42:雨水收集器

想象你被困在一个由柱子组成的迷宫中,每个柱子都有不同的高度。一场倾盆大雨过后,这些柱子形成了大小不一的容器,你必须计算出每个容器可以收集多少雨水。听起来很有趣,不是吗?这就是 LeetCode 42 问题的精髓所在。

雨水收集难题

LeetCode 42 要求我们计算给定柱子阵列中每个柱子可以收集的最大雨水量。为了解决这个问题,我们将踏上一个算法之旅,使用栈这个数据结构作为我们的有力助手。

栈的妙用

栈是一种先进后出的数据结构,非常适合解决 LeetCode 42 问题。我们将使用栈来存储柱子的索引,这些索引代表着容器的左边界。

当我们从左到右遍历柱子时,我们将遇到的情况分为两种:

  1. 遇到比栈顶柱子更高的柱子: 我们将该柱子的索引压入栈中,因为它可以作为更高容量容器的左边界。
  2. 遇到比栈顶柱子更低的柱子: 这表明当前柱子可以作为容器的右边界。我们弹出栈顶元素并计算出它所代表的容器可以收集的雨水量。

算法流程

  1. 使用栈存储柱子索引。
  2. 遍历柱子数组,从左到右。
  3. 如果当前柱子更高,则将其索引压入栈中。
  4. 如果当前柱子更低,则弹出栈顶元素并计算雨水量。
  5. 重复步骤 3 和 4,直到遍历完所有柱子。

算法复杂度

该算法的时间复杂度为 O(n),其中 n 是柱子的数量。这是因为我们只需要遍历一次柱子数组。空间复杂度也是 O(n),因为栈的最大大小为柱子的数量。

JavaScript 实现

/**
 * LeetCode 42: 接雨水
 *
 * 使用栈来计算每个柱子最多能接多少雨水。
 *
 * @param {number[]} height 柱子的高度
 * @return {number} 最多能接多少雨水
 */
const trap = (height) => {
  // 使用栈来存储柱子的索引
  const stack = [];
  // 最多能接多少雨水
  let maxWater = 0;

  for (let i = 0; i < height.length; i++) {
    // 当遇到一个比栈顶元素更高的柱子时,将其索引压入栈中
    while (stack.length > 0 && height[i] > height[stack[stack.length - 1]]) {
      // 弹出栈顶元素
      const top = stack.pop();
      // 如果栈不为空,则可以计算出可以接住的雨水量
      if (stack.length > 0) {
        const width = i - stack[stack.length - 1] - 1;
        const height = Math.min(height[i], height[stack[stack.length - 1]]) - height[top];
        maxWater += width * height;
      }
    }
    // 将当前柱子的索引压入栈中
    stack.push(i);
  }

  return maxWater;
};

例子

让我们考虑一个柱子高度数组:

height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]

按照算法步骤,我们首先将栈初始化为空。然后,从左到右遍历柱子数组:

  • 当我们遇到 1 时,我们将其索引 1 压入栈中。
  • 当我们遇到 0 时,我们将其索引 2 压入栈中。
  • 当我们遇到 2 时,我们将其索引 3 压入栈中。
  • 当我们遇到 1 时,我们将其索引 4 压入栈中。
  • 当我们遇到 0 时,我们将其索引 5 压入栈中。
  • 当我们遇到 1 时,我们将其索引 6 压入栈中。
  • 当我们遇到 3 时,它比栈顶元素 1 高。因此,我们将其索引 7 压入栈中。
  • 当我们遇到 2 时,它比栈顶元素 3 低。因此,我们弹出栈顶元素 3 并计算雨水量。雨水量为 (7 - 6 - 1) * (min(3, 2) - 0) = 3。
  • 当我们遇到 1 时,我们将其索引 8 压入栈中。
  • 当我们遇到 2 时,它比栈顶元素 1 高。因此,我们将其索引 9 压入栈中。
  • 当我们遇到 1 时,我们将其索引 10 压入栈中。

遍历完所有柱子后,栈中的元素为 [2, 6, 9, 10]。这意味着最后有四个容器,分别收集了 3、0、2 和 0 单位的雨水。因此,总共收集的雨水量为 3 + 0 + 2 + 0 = 5。

常见问题解答

  1. 为什么需要使用栈?
    栈可以帮助我们有效地跟踪柱子高度并计算雨水量。它允许我们弹出栈顶元素并计算出较低柱子可以收集的雨水量。
  2. 是否需要考虑边界条件?
    由于栈中始终存储着容器的左边界,因此我们不必担心边界条件。
  3. 时间复杂度是多少?
    算法的时间复杂度为 O(n),其中 n 是柱子的数量。
  4. 空间复杂度是多少?
    算法的空间复杂度为 O(n),因为栈的最大大小为柱子的数量。
  5. LeetCode 42 的变体有哪些?
    存在许多 LeetCode 42 的变体,例如:
    • 接雨水 II:考虑二维柱子矩阵。
    • 接雨水 III:考虑具有不同高度和方向的柱子。