JS算法-接雨水

298 阅读1分钟

本题来自leetcode 第 42 题。解题方法和代码来自leetcode解题分析

题目描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

image.png

输入: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
0 <= n <= 3 * 104
0 <= height[i] <= 105

思路解法

  1. 动态规划
  • 找到数组中从下标 i 到最左端最高的条形块高度 {left_max}
  • 找到数组中从下标 i 到最右端最高的条形块高度 {right_max}
  • 扫描数组 {height}
  • 累加 Math.min({max_left}, {max_right}) - {height}到 ans 上。

代码演示

方法一:动态规划

image.png

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
  const len = height.length;
  if (len < 3) return 0;
  const left = [];
  const right = [];
  left[0] = height[0];
  right[len - 1] = height[len - 1];
  let res = 0;
  for (let i = 1; i < len; i++) {
    left[i] = Math.max(left[i - 1], height[i]);
  }
  for (let i = len - 2; i >= 0; i--) {
    right[i] = Math.max(right[i + 1], height[i]);
  }
  for (let i = 0; i < len; i++) {
    res += Math.min(left[i], right[i]) - height[i];
  }
  return res;
};

复杂性分析:

时间复杂度:O(n)。

存储最大高度数组,需要两次遍历,每次 O(n) 。
最终使用存储的数据更新ans,O(n)。

空间复杂度:O(n)。

和方法 1 相比使用了额外的 O(n)空间用来放置 left_max 和 right_max 数组。

方法二:栈

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
  if (height.length < 3) return 0;
  const len = height.length;
  const stack = [];
  let res = 0, i = 0;
  while (i < len) {
    while (stack.length && height[i] > height[stack[stack.length - 1]]) {
      const top = stack.pop();
      if (!stack.length) break;
      const width = i - stack[stack.length - 1] - 1;
      const depth = Math.min(height[i], height[stack[stack.length - 1]]) - height[top];
      res += width * depth;
    }
    stack.push(i++);
  }
  return res;
};

复杂性分析:

时间复杂度:O(n)。

空间复杂度:O(n)。

方法三:双指针

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
  if(height.length < 3) return 0;
  let left = 0, right = height.length - 1;
  let left_max = 0, right_max = 0, res = 0;
  while (left < right) {
    if (height[left] < height[right]) {
      if (height[left] > left_max) {
        left_max = height[left];
      } else {
        res += left_max - height[left];
      }
      left++;
    } else {
      if (height[right] > right_max) {
        right_max = height[right];
      } else {
        res += right_max - height[right];
      }
      right--;
    }
  } 
  return res;
}

复杂性分析:

时间复杂度:O(n)。

空间复杂度:O(1)。

总结

三种编程思想需要好好理解消化。性能来说,第三种方式空间复杂度更优。