前端算法必刷题系列[80]

469 阅读3分钟

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

这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。

148. 接雨水 (trapping-rain-water)

标签

  • 双指针
  • 困难

题目

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

基本思路

我们拿到一个问题,如果一时想不到其他好方法,不要愣着,能解出问题,低效的代码也行,先解出来!任何题目大多又有暴力方式,思考简单,但效率不高。

我们先看穷举法,简单来说就是遍历每个位置能放下的水有多少,如图

image.png

i,的位置能放下的水是,左边最高墙 l_maxH = 3,右边最高 r_maxH = 4,木桶原理取小的那个,再减去当前 i柱子的高度就得到盛水的体积了,就是 3 - 1 = 2

代码也非常暴力

var trap = function(height) {
  let res = 0, len = height.length
  if (len === 0) return 0

  // 其实就是遍历寻找每个位置上左右最高的位置
  for (let i = 1; i < len-1; i++) {
    let l_maxH = 0
    let r_maxH = 0

    // 找右边最高的柱子
    for (let j = i; j < len; j++) {
      // 每轮比较更新最大值, 最后就是该位置上右边最高柱子
      r_maxH = Math.max(r_maxH, height[j])
    }

    // 同样的,找左边最高的柱子
    for (let k = i; k >= 0; k--) {
      // 每轮比较更新最大值, 最后就是该位置上右边最高柱子
      l_maxH = Math.max(l_maxH, height[k])
    }

    // 对比两边柱子的最小值,木桶原理,取小的那个,
    // 然后减去当前高度就是该位置上能存的水量
    res += Math.min(l_maxH, r_maxH) - height[i]
  }

  return res
}

let height = [0,1,0,2,1,0,1,3,2,1,2,1]
console.log(trap(height))

复杂度 O(n^2) 那么有什么方法去优化呢

双指针

image.png

其实整体思路大致相似,有两个指针从两头向中间逼近,如图,left 指针坐标位置能承载的水量,其实只跟 l_maxH 有关,跟 r_maxH 是无关的。所以只要动态更新左右最高墙还是取木桶短板做墙,算出每个位置的盛水量即可。

双指针

var trap = function(height) {
  let len = height.length, res = 0
  if (len === 0) return 0
  // 双指针一左一右放边界坐标上, left -> 从左往右,right 反之
  let left = 0
  let right = len - 1
  // 左边的最大值,从左往右遍历获得
  let l_max = height[0]
  // 右边的最大值,从右往左遍历获得
  let r_max = height[len-1]
  while (left <= right) {
    l_max = Math.max(l_max, height[left])
    r_max = Math.max(r_max, height[right])
    // 取短的墙
    if (l_max <= r_max) {
      res += l_max - height[left]
      left++
    } else {
      res += r_max - height[right]
      right--
    }
  }
  return res
};

另外向大家着重推荐下这个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友 Or 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考