一起刷LeetCode——接雨水(双指针/单调栈)

109 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

接雨水

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

来源:力扣(LeetCode)链接:leetcode.cn/problems/tr…

分析

  • 水存在于柱子之间,如果两个相邻柱子高度一个大一个小,那接水量是由小值来决定的,接水量=较小值。因此在针对数组时,面对某个下标i,需要向左、向右分别遍历,找到接水量,每个位置分别取最小值累积,即可得到总的接水量

优化

使用双指针优化

  • 在向左、向右遍历算当前位置的接水量时,需要维护两个数组。但是最后相加的项是这两个数组中下标为i的最小值,所以只需要左右方向的最大值和双指针LR,可以有效降低空间复杂度,且数组只遍历一遍。
  • LR的移动,更新leftMax和rightMax,当左边小于右边,那左边接到的雨水是算到总接水量的,所以左指针移动,右指针移动逻辑类似。
代码
/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    let ans = 0
    let left = 0
    const len = height.length
    let right = len -1
    let leftMax = 0
    let rightMax = 0
    while(left<right) {
        leftMax = Math.max(leftMax, height[left])
        rightMax = Math.max(rightMax, height[right]) 
        if(leftMax < rightMax) {
            ans += leftMax - height[left]
            left ++
        } else {
            ans+= rightMax - height[right]
            right --
        }
    }
    return ans
};

另一种分析

  • 把y轴当做基准轴,记录x下标,根据heght[x]和当前最大的值坐标对应的位置,坐标相减乘高度差也可得到接水量
  • 因为涉及到最大值的坐标,每次都能直接获取到最大值,可以维护一个单调栈,栈顶元素即最大值
/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    let stack = []
    let ans = 0
    const len = height.length
    for(let i=0;i<len;i++) {
        while(stack.length && height[i] > height[stack[stack.length -1]]) {
            const top = stack.pop()
            if(!stack.length) {
                break
            }
            const left = stack[stack.length -1]
            const curWidth = i-left-1
            const curHeight = Math.min(height[left], height[i]) - height[top]
            ans += curWidth * curHeight
        }
        stack.push(i)
    }
    return ans
};

总结

  • 做这道题的时候,关于接雨水的量,想到了短板原理,所有板子变得一样一样高才能盛更多的谁,加强自己薄弱的地方,可以变得更强
  • 今天又是有收获的一天