题目链接:https://leetcode.cn/problems/trapping-rain-water/
相同题<面试题 17.21. 直方图的水量>链接:https://leetcode.cn/problems/volume-of-histogram-lcci/
思路
方法1:暴力
直观想法:
遍历数组,对于数组中的每个元素,我们找出下雨后水能达到的最高位置Min(Left, Right),等于两边最大高度的较小值减去当前高度的值。
算法:
- 初始化 ans=0
- 从左往右扫描数组
。初始化 max_Left = 0和 max_Right = 0
。从当前元素向左扫描,找到最高点
- max_Left = max(max_Left, height[j])
。从当前元素向右扫描,找到最高点
- max_Right = max(max_Right , height[j])
。将 max(0, min(max_Left, max_Right) - height[i]) 累加到ans
代码示例
func trap(height []int) int { ans := 0 min := func(a, b int) int{ if a > b{ return b } return a } max := func(a, b int) int{ if a > b{ return a } return b } for i := 1; i < len(height) - 1; i++{ max_Left, max_Right := 0, 0 for j := 0; j < i; j++{ max_Left = max(max_Left, height[j]) } for j := i + 1; j < len(height); j++{ max_Right = max(max_Right, height[j]) } ans += max(0, min(max_Left, max_Right) - height[i]) } return ans }
复杂度分析
- 时间复杂度:O(n2) 每个元素都要从左往右扫描一遍数组
- 空间复杂度:O(1)
方法2:动态编程
直观想法:
既然我们每个元素都要从左往右扫描一遍数组找两边的最大值,不如我们提前把左右两边的最大值存入数组,用空间换时间的方法。
算法
- 找到数组从下标 i 到最左端最高的条形块高度max_Left
- 找到数组从下标 i 到最右端最高的条形块高度max_Right
- 遍历一次数组
。将 max(0, min(max_Left[i], max_Right[i]) - height[i]) 累加到ans
代码示例
func trap(height []int) int { lm := make([]int, len(height)) rm := make([]int, len(height)) min := func(a, b int) int{ if a > b{ return b } return a } max := func(a, b int) int{ if a < b{ return b } return a } for i := range height{ if i == 0{ lm[i] = height[i] }else{ lm[i] = max(height[i], lm[i - 1]) } } for i := len(height) - 1; i >= 0; i--{ if i == len(height) - 1{ rm[i] = height[i] }else{ rm[i] = max(height[i], rm[i + 1]) } } ans := 0 for i := range height{ ans += max(0, min(lm[i], rm[i]) - height[i]) } return ans }
复杂度分析
- 时间复杂度:O(n)
。存储最大高度数组,需要两次遍历,每次 O(n)
。最后更新ans遍历数组,也是O(n)
- 空间复杂度:O(n)
。和方法 1 相比使用了额外的 O(n)空间用来放置 max_Left 和 max_Right 数组。
参考链接:https://leetcode.cn/problems/trapping-rain-water/solution/jie-yu-shui-by-leetcode/