【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
相关推荐
ZC跨境爬虫1 小时前
跟着 MDN 学JavaScript day_5:技能测试——变量实战
java·开发语言·前端·javascript
瑞雪兆丰年兮1 小时前
[0开始学Java|第二十四天]集合(Map&可变参数&集合工具类Collections)
java·开发语言·map·collections
鱼鳞_1 小时前
苍穹外卖-Day12(数据统计)
java·spring boot
phltxy1 小时前
Spring AI Alibaba 多模态应用开发实践
java·人工智能·spring
garmin Chen1 小时前
Prompt工程入门:让AI按你的要求工作(2)--Prompt 高阶优化与结构化设计
java·人工智能·python·ai·prompt
GesLuck1 小时前
Node-RED企业微信发送—群文件
android·java·企业微信
南境十里·墨染春水1 小时前
讲讲移动语义
算法
whatever who cares1 小时前
android中fragment demo举例
android·java·开发语言
西凉的悲伤1 小时前
Guava类库——Range连续区间
java·算法·guava