LeetCode算法学习之--Stack--接雨水

453 阅读2分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

大家好今天给大家分享下一道 LeetCode 困难难度 的题目接雨水

题目

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

示例 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 个单位的雨水(蓝色部分表示雨水)。 



输入:height = [4,2,0,3,2,5]
输出:9

分析

1.柱子宽度为1,柱子的高度都为整数,且可能有重复值

2.观察发现雨水的面积等于 两个柱子之间的宽度 *(两个柱子的最小值-洼地的高度)

3.返回雨水面积的总和

解法

1.stack

2.暴力法

解法一:stack

思路
1.stack维护一个单调递减的栈,迭代height,当height[i]遇到比stack顶大的就开始计算面积,步骤如下
  1.栈顶出栈,作为洼地底部高度 top
  2.剩下的栈顶作为洼地的边界
  3.height[i]作为右边界
  4.水池的高度为 Math.min(left,right)-top,这里面考虑了一种特性情况
    1.因为可能有重复值,所以当left的高度和top的高度一样,通过计算那么高度就为0,面积也就为零
    仅仅当left的高度大于top时候水池才会有高度
  5.水池的宽度为 i-left索引-1
  6.因为需要总面积所以需要累加
 2.返回总面积 
*/

var trap = function (height) {
  // stack 存放索引
  const stack = [];
  let area = 0;
  // 存放水池的的左边界
  const left = [];
  // 存放水池的右边边界
  const right = [];
  // 存放水池的底部高度
  const bottom = [];
  for (let i = 0; i < height.length; i++) {
    //如果遇到stack的顶部元素小于新来的元素,说明遇到了水池的右边界
    while (stack.length && height[stack[stack.length - 1]] < height[i]) {
      //获取水池底部高度
      const poolBottom = stack.pop();
      // 但是如果stack为空,说明没有左边界,则结束循环
      if (!stack.length) {
        break;
      }
      //取得水池的 左边界索引
      bottom.push(poolBottom);
      //取得水池的 右边界索引
      left.push(stack[stack.length - 1]);
      //取得水池的底部索引
      right.push(i);
    }

    stack.push(i);
  }

  // 迭代计算水池面积的总和
  for (let i = 0; i < left.length; i++) {
    const leftHeight = height[left[i]];
    const rightHeight = height[right[i]];
    const bottomHeight = height[bottom[i]];

    // 取左右边界的最小值 - 底部的高度= 水池的蓄水高度
    const accHeight = Math.min(leftHeight, rightHeight) - bottomHeight;
    const width = right[i] - left[i] - 1;
    area += accHeight * width;
  }

  return area;
};

/* 复杂度
时间 O(n)
空间 最大 O(n-2) 最小O(1)
*/

解法二:暴力法(无法通过 仅仅是提供思路)

思路
1.迭代没个柱子的高度 i
2.如果能找到比height[i]高的左右边界则表明可以蓄水,步骤如下
  1.在i的左边寻找边界,如果有比height[i]高的,且是最高的左边界 则记录下来 leftHeight
  2.在i的右边寻找边界,如果有比height[i]高的,且是最高的右边界 则记录下来 rightHeight
  3.计算高度差 curHeight=(Math.min(leftHeight,rightHeight)-height[i])
  4.计算面积 area = curHeight * 1(柱子高度)
3.重复上面的计算过程,在累加求出面积总和
*/

var trap = function (height) {
  let area = 0;
  for (let i = 0; i < height.length; i++) {
    let leftHeight = -1;
    // 求最高的左边界
    for (let j = i; j > -1; j--) {
      if (height[j] > height[i]) {
        leftHeight = Math.max(height[j], leftHeight);
      }
    }

    // 求最高右边界
    let rightHeight = -1;
    for (let j = i; j < height.length; j++) {
      if (height[j] > height[i]) {
        rightHeight = Math.max(height[j], rightHeight);
      }
    }

    if (leftHeight === -1 || rightHeight === -1) {
      continue;
    }

    const curHeight = Math.min(leftHeight, rightHeight) - height[i];
    area += curHeight * 1;
  }

  return area;
};

/* 复杂度
时间 O(n^2)
空间 O(1)
*/

总结

这道题考察的是如果应用stack来寻找左右边界

大家可以看看我分享的一个专栏(前端搞算法)里面有更多关于算法的题目的分享,希望能够帮到大家,我会尽量保持每天晚上更新,如果喜欢的麻烦帮我点个赞,十分感谢

大家如果对“TS”感兴趣的可以看看我的专栏 (TypeScript常用知识),感谢大家的支持

文章内容目的在于学习讨论与分享学习算法过程中的心得体会,文中部分素材来源网络,如有侵权,请联系删除,邮箱 182450609@qq.com