题目:42. 接雨水
题目描述:
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
来源:https://leetcode-cn.com/problems/trapping-rain-water/
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
思路:(反正我一个都没想出来)
方法一:暴力
核心就是找到每一个矩形上方能够装多少水,那么对于任意一个矩形,怎么算它上方装的水呢?根据木桶效应可以知道是由它两边最高的矩形决定的:具体的思路看图
这样遍历一遍列表,分别找到每个元素的left_max和right_max,计算每个矩形承载的水量,求和即可,时间复杂度为O(n^2),实测不能AC。
方法二:动态编程(官方题解这么叫)
是对暴力法的改进,典型的空间换时间,既然每个元素都要计算left_max和right_max,那么就
两个数组left_max和right_max分别记录每个元素左边和右边的最大值。然后根据这两个数组,计算每个矩形承载的水量,相当于把原数组遍历了三次,时间复杂度为O(n)。
方法三:单调栈
注意栈里面存放的是元素的序号,然后递减是按照元素大小来比较的。单调递减栈,主要思想就,遍历到元素i时,如果height[i]大于栈顶元素,把栈顶元素当作盛水的底,来计算栈顶元素的盛水量。如下图:
弹出栈顶元素并处理完成之后,继续比较栈顶和当前元素的大小,如下图:
还有一个要注意的点就是,弹出栈顶之后,要判断栈是否为空,栈为空则退出循环。因为弹出栈顶后若栈为空,相当于只有底边和右边,左边没有,存不住水,如下图所示:
方法四:双指针
核心还是计算每个矩形上方能够承载的水量。动态编程使用两个数组来记录每个元素左边的最大值和右边的最大值,这里进一步优化,只使用两个数来记录。
当左边最大值大于右边最大值时,左边不动,右指针向左遍历,遇到比右边最大值还要大的就更新右边最大值,遇到比右边最大值小的,相当于左边最大值、右边最大值和遍历到的值三者形成了凹陷,可以储水,y因为是在left_max > right_max的条件下,故储水量用right_max-height[i]计算即可。left_max < right的情况同理,不再赘述。
说的不是明白,详细解释都在代码里了,show you the code.
代码:
(1)暴力法:
class Solution:
def trap(self, height: List[int