【Leetcode】 接雨水

1 题目

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

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

示例 2:

输入:height = 4,2,0,3,2,5

输出:9

2 分析

首先,从图示可以看出,数组总的接水量等于每个位置接水量的和。而每个位置的接水量取决于它左右两边柱子的高度(准确的说,是取决于它左边最高的柱子和右边最高的柱子中较低的那个)。这样它左右两边和它自己就可以形成一个容器来盛水。因此,最暴力的解法是遍历每一个位置i,然后从这个位置出发,分别向左右两边寻找最高的柱子leftMax和rightMax,然后计算得到盛水量min(leftMax,rightMax)-heighti

python 复制代码
def trap(self, height):
    """
    :type height: List[int]
    :rtype: int
    """
    n = len(height)
    res = 0
    for i in range(1,n-1):
        leftMax = max(height[:i])
        rightMax = max(height[i+1:])
        tmp = min(leftMax,rightMax)-height[i]
        if tmp>0:
            res += tmp
    return res

但上面这种方式时间复杂度很高,而且其实有很多重复工作。例如,对于第i+1个位置而言,其左边的leftMax其实只需要看第i个位置左边的leftMax与heighti的大小。

python 复制代码
def trap(self, height):
    """
    :type height: List[int]
    :rtype: int
    """
    n = len(height)
    res = 0
    leftMax = height[0]
    for i in range(1,n-1):
        leftMax = max(height[i-1],leftMax)
        rightMax = max(height[i+1:])
        cur = min(leftMax,rightMax)-height[i]
        if cur>0:
            res += cur
    return res

但依然超出时间限制,因为右边rightMax还是有重复工作。因此,考虑把leftMax的递归思想引入rightMax中,先从右向左计算每个位置的rightMax并用数组记录,后续再遍历计算每个位置的雨水量的时候,就可以直接取这部分信息使用了。

python 复制代码
def trap(self, height):
    """
    :type height: List[int]
    :rtype: int
    """
    n = len(height)
    res = 0
    leftMax = height[0]
    rightMax = height[n-1]
    rightMaxList = [0]*n
    for i in range(n-1,0,-1):
        rightMax = max(height[i],rightMax)
        rightMaxList[i] = rightMax

    for i in range(1,n-1):
        leftMax = max(height[i-1],leftMax)
        rightMax = rightMaxList[i+1]
        cur = min(leftMax,rightMax)-height[i]
        if cur>0:
            res += cur
    return res

上面的方法提交成功,终于不超时了,但只击败了5%。需要思考下还有没有别的优化点。

有一个关键点是,每次计算位置i的盛水量,其实只用到了leftMax和rightMax中较小的那个。如果我们明确知道位置i的右边有个很大的值,超出了其左边的最大值,那么就可以只用leftMax的信息。同理,如果我们明确知道位置i的左边有个很大的值,超出了其右边的最大值,那么就可以只用rightMax的信息。而每一个时刻,leftMax和rightMax总有大小,因此总有位置的盛水量可以被计算,不是左边就是右边。

换一种方式说,如果有两个指针left和right分别从左向右、从右向左移动,leftMax表示height:left的最大值,heightright:的最大值。那么,如果明确leftMax<rightMax,其实已经可以更新left位置的盛水量了(因为rightMax只会增大不会减少了:因为left<right,从而heightleft:>=heightright:)。同理,如果明确leftMax>rightMax,其实已经可以更新right位置的盛水量了(因为leftMax只会增大不会减少了:因为left<right,从而height:right>=height:left)。如果明确leftMax=rightMax,那么更新left和right都可以,因为它俩都有一边是会只增不减的。这样我们就可以一边移动双指针,一边更新盛水量,一遍维护leftMax和rightMax了。

python 复制代码
def trap(self, height):
    """
    :type height: List[int]
    :rtype: int
    """
    n = len(height)
    res = 0
    left, right = 1, n-2
    leftMax = height[0]
    rightMax = height[n-1]
    while left <= right:
        if leftMax < rightMax:
            cur = leftMax-height[left]
            leftMax = max(height[left],leftMax)
            left += 1
            res += max(cur,0)
        else:
            cur = rightMax-height[right]
            rightMax = max(height[right],rightMax)
            right -= 1
            res += max(cur,0)
    return res
相关推荐
要开心吖ZSH1 天前
一文搞懂:JDK 21 虚拟线程 vs N种异步方案,到底该怎么选?
java·数据库·jdk·虚拟线程
guodingdingh1 天前
软件开发工作问题总结0718
java·开发语言·数据库
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式
GuWenyue1 天前
传统Agent工具两大痛点!300行代码落地MCP跨语言工具,彻底解耦LLM与工具
前端·人工智能·算法
优橙教育1 天前
5G网优培训 vs Java开发:转行选哪个?
java·开发语言·5g
糖果店的幽灵1 天前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
我叫洋洋1 天前
C ++ [ hello world ]
c语言·c++·算法
腻害兔1 天前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
碎光拾影1 天前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
奋发向前wcx1 天前
y1,y2总复习笔记5 2026.7.19
数据结构·笔记·算法