Leecode42——接雨水

2024.3.19

Leecode42.接雨水

给定 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

提示:

·n == height.length

·1 <= n <= 2 * 104

·0 <= height[i] <= 105


个人思路

第一次挑战一下leecode的困难题,看评论说是大名鼎鼎。

自以为想到了一个很巧妙的解法,结果可惜超时了,不过还是说一下,因为是我觉得最好理解的一种思路。

接雨水其实可以每一行每一行看:

要求每一行的雨水量,我们需要找两个数:(1)每一行最边缘在哪:(2)每一行柱子占了几个格子。边缘找到了,长度就找到了,把柱子占的格子减掉就是雨水的量。

根据这个思路,我们先找到最高的柱子,那么我们需要从(0,height_max)中循环,每一层判断height数组中,比当前高度低的置0,高的置1,然后得到这一层的情况,我把它叫做board。

然后按照刚才的逻辑,找两个数,就可以求出每一层的雨水量,最后叠加就是总量。

class Solution:
    def trap(self, height: List[int]) -> int:
        height_max = max(height)
        layer = []
        for i in range(0, height_max):
            board = []
            # 每一层单独看
            for j in range(0, len(height)):
                if i<height[j]:
                    board.append(1)
                else:
                    board.append(0)
            layer.append(per_layer(board))
        return sum(layer)

def per_layer(board):
    left = 0
    right = len(board)-1
    while(board[left]!=1):
        left = left+1
    while(board[right]!=1):
        right = right-1
    num_one = sum(board)
    num_zeros = right-left+1-num_one
    return num_zeros

但是很遗憾:

想了想,也没找到优化的办法,不知道有没有大神能帮帮忙。


官方题解学习

(题难就多学习)

1.动态规划

这里直接搬运一下官方的思路:

class Solution:
    def trap(self, height: List[int]) -> int:
        if not height:
            return 0
        
        n = len(height)
        leftMax = [height[0]] + [0] * (n - 1)
        for i in range(1, n):
            leftMax[i] = max(leftMax[i - 1], height[i])

        rightMax = [0] * (n - 1) + [height[n - 1]]
        for i in range(n - 2, -1, -1):
            rightMax[i] = max(rightMax[i + 1], height[i])

        ans = sum(min(leftMax[i], rightMax[i]) - height[i] for i in range(n))
        return ans

确实好理解,也有点巧妙,不需要循环套循环,复杂度只有O(n)。

这里关键还是在通过什么方式去计算水量,这个是按列计算,自己写的是按行计算,也是自己第一次见动态规划。

个人对动态规划的理解,其实还是动中有静,虽然有很多东西在变,但是我们可以预先求出来这些变化,化动为静。


2.单调栈

(又是第一次接触到的)

单调栈

class Solution:
    def trap(self, height: List[int]) -> int:
        ans = 0
        stack = list()
        n = len(height)
        
        for i, h in enumerate(height):
            while stack and h > height[stack[-1]]:
                top = stack.pop()
                if not stack:
                    break
                left = stack[-1]
                currWidth = i - left - 1
                currHeight = min(height[left], height[i]) - height[top]
                ans += currWidth * currHeight
            stack.append(i)
        
        return ans

其实看动图能明白,跟自己想的思路特别像,只是用单调栈这种数据结构更简单的实现了,太难想到了。


3.双指针

class Solution:
    def trap(self, height: List[int]) -> int:
        ans = 0
        left, right = 0, len(height) - 1
        leftMax = rightMax = 0

        while left < right:
            leftMax = max(leftMax, height[left])
            rightMax = max(rightMax, height[right])
            if height[left] < height[right]:
                ans += leftMax - height[left]
                left += 1
            else:
                ans += rightMax - height[right]
                right -= 1
        
        return ans

看了过后只能说巧妙,妙不可言。从来没想到过这种思路。


总结:

算法的操作总结不了一点。。。

这个题要想写出代码,一定要理解每一个方法的动画,形成动画才能给出解答!

第一次做困难题,道阻且长啊。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值