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
看了过后只能说巧妙,妙不可言。从来没想到过这种思路。
总结:
算法的操作总结不了一点。。。
这个题要想写出代码,一定要理解每一个方法的动画,形成动画才能给出解答!
第一次做困难题,道阻且长啊。