音乐
暂未播放
LeetCode | 接雨水
分类:双指针 / 数组
题目#
给定一个非负整数数组 height,每个数字表示一个柱子的高度。
下雨之后,柱子之间可能会积水。求最多可以接多少水。

示例:
1输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]2输出:6讲解#
建议:先自己想一下“每个位置为什么能接水”,再看解析。否则双指针这一步会比较抽象。
这道题不要先想着“一整块水”怎么算,而是先看每一个位置能接多少水。
一个位置能接多少水,取决于它左右两边的挡板:
具体来说:
1当前位置能接的水 = min(左边最高高度, 右边最高高度) - 当前高度如果这个值小于 0,就说明这个位置接不了水。
比如某个位置高度是 1,它左边最高是 3,右边最高是 2,那么它最多只能接到高度 2,因为右边挡板更矮。
所以这个位置能接:
1min(3, 2) - 1 = 1暴力做法是:对每个位置,都向左找一次最高柱子,再向右找一次最高柱子,然后计算这个位置能接多少水。这样可以做出来,但是每个位置都要左右扫描,时间复杂度是 O(n^2)。
双指针的目标就是避免重复扫描。
我们让 left 从左往右走,right 从右往左走,同时维护:
1left_max = left 左侧已经见过的最高柱子2right_max = right 右侧已经见过的最高柱子关键问题是:什么时候可以确定某个位置能接多少水?
当 height[left] < height[right] 时,可以先结算 left 这个位置。
原因是:右边此时至少有 height[right] 这根柱子,而且它比 height[left] 高。也就是说,left 右侧不是完全没有挡板,右边挡板已经够用了。
所以 left 位置能接多少水,主要取决于左边最高的柱子 left_max。
此时可以计算:
1left_max - height[left]然后让 left 往右移动。
反过来,当 height[left] >= height[right] 时,可以先结算 right 这个位置。
原因是:左边此时至少有 height[left] 这根柱子,而且它不低于 height[right]。所以 right 左侧挡板已经够用了。
此时 right 位置能接多少水,主要取决于右边最高的柱子 right_max。
此时可以计算:
1right_max - height[right]然后让 right 往左移动。
这就是双指针的核心:每次只结算“较矮的一边”。因为较矮的一边已经能确定自己的另一侧有挡板,不需要再等更远的位置。
复杂度#
- 暴力做法:
- 时间复杂度:
O(n^2) - 空间复杂度:
O(1)
- 时间复杂度:
- 双指针做法:
- 时间复杂度:
O(n) - 空间复杂度:
O(1)
- 时间复杂度:
每个指针只会向中间移动,不会回头,所以总移动次数最多是 n 次。
代码#
源码#
1from typing import List2
3
4class Solution:5 def trap(self, height: List[int]) -> int:6 ans = 07 left = 08 right = len(height) - 19 left_max = 010 right_max = 011
12 while left < right:13 left_max = max(left_max, height[left])14 right_max = max(right_max, height[right])15
16 if height[left] < height[right]:17 ans += left_max - height[left]18 left += 119 else:20 ans += right_max - height[right]21 right -= 122
23 return ans测试#
1from src.python._042_Trapping_Rain_Water import Solution2
3
4sol = Solution()5
6assert sol.trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]) == 67assert sol.trap([4, 2, 0, 3, 2, 5]) == 98assert sol.trap([1, 2, 3]) == 09assert sol.trap([]) == 010
11print("PASS")参考资料#
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分内容可能已过时
评论区
分享你的想法,与大家交流讨论
音乐
暂未播放



