文章目录
- 引言
- 接雨水
- 题目描述
- 提示
- 解决方案3:【双指针】
- 结束语
接雨水
【LeetCode刷题笔记(8-1)】【Python】【接雨水】【动态规划】【困难】
【LeetCode刷题笔记(8-2)】【Python】【接雨水】【单调栈】【困难】
引言
编写通过所有测试案例的代码并不简单,通常需要深思熟虑和理性分析。虽然这些代码能够通过所有的测试案例,但如果不了解代码背后的思考过程,那么这些代码可能并不容易被理解和接受。我编写刷题笔记的初衷,是希望能够与读者们分享一个完整的代码是如何在逐步的理性思考下形成的。我非常欢迎读者的批评和指正,因为我知道我的观点可能并不完全正确,您的反馈将帮助我不断进步。如果我的笔记能给您带来哪怕是一点点的启示,我也会感到非常荣幸。同时,我也希望我的分享能够激发您的灵感和思考,让我们一起在编程的道路上不断前行~
接雨水
题目描述
给定 n
个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
图片来源
提示
- n == height.length
- 1 <= n <= 2 * 104
- 0 <= height[i] <= 105
解决方案3:【双指针】
在【LeetCode刷题笔记(8-1)】【Python】【接雨水】【动态规划】【困难】和【LeetCode刷题笔记(8-2)】【Python】【接雨水】【单调栈】【困难】中,我们详细阐述了如何在一步步理性分析下基于【动态规划】和【单调栈】设计完整的算法和代码通过接雨水的所有测试用例。这两种算法的空间复杂度都是O(n),其中动态规划法需要额外维护两个长度为N的数组left_max_list
和right_max_list
,用于记录每个位置i
,其左侧柱子的最大高度left_max_list[i]
和右侧柱子的最大高度right_max_list[i]
。
具体来说,我们首先从左到右完整地遍历数组height
得到left_max_list
,然后再从右往左完整地遍历数组height
得到right_max_list
。实际上,这相当于把【接雨水】问题的解决策略分成两个相对独立的步骤:
-
获取每个位置
i
,其左侧和右侧柱子的最大高度。这一步需要两个for循环,分别从左到右和从右到左遍历数组。 -
根据木桶原理,以及步骤1获取的必要信息,得到最终的雨水总量。这一步还需要一个for循环,遍历整个数组。
问题1:有没有其它策略能够把步骤一和步骤二结合起来,即在一个循环下解决【接雨水】问题?
有,双指针。
在上面的分析过程中,我们发现步骤1的两个循环,一个是从左往右,一个是从右往左,这种遍历方法和双指针非常类似。因此,我们考虑是否可以用左指针替代从左往右的循环,同时用右指针替代从右往左的循环。这样,我们可以在一次遍历中完成两个步骤,提高算法的效率。
不仅可以,而且还买一送一,顺带解决了获取位置i
雨水量的问题。
1. 当左指针left
从左往右的移动过程中,我们可以通过left_max
记录区间[0,left]
的最大高度 。同理,当右指针right
从右往左的移动过程中,我们也可以通过right_max
记录区间[right,n-1]
的最大高度。
关键问题2:如何决定下一步是移动左指针还是右指针?
当能确定位置left
的接水量时,left
右移;当能确定位置right
的接水量时,right
左移;
对于当前左指针的位置left
,[0, left]区间的最大高度left_max
是已知的,并且[right,n-1]
区间的最大高度right_max
也是已知的。根据木板原理,一个木桶的装水量总是受限于其最短的那块木板 ⇒ 此时left_max<right_max
成立 ⇒ [0, left]的最高左木板 < [right, n-1]区间的最高右木板 <= [left+1, n-1]区间的最高右木板 恒成立⇒ 位置left
的装水量由[0, left]的最高左木板决定,而[0, left]的最高左木板已经是板上钉钉 ⇒ 自然可以计算位置left
的接水量water_left = left_max - height[left]
;既然位置left
的接水量已经确定,下一步自然是left
右移。
同理,对于当前右指针的位置right
,[0, left]区间的最大高度left_max
是已知的,并且[right,n-1]
区间的最大高度right_max
也是已知的 ⇒ 此时right_max < left_max
成立 ⇒ [right, n-1]区间的最高右木板 < [0, left]的最高左木板 <= [0,right-1]区间的最高左木板 恒成立⇒ 位置right
的装水量由 [right, n-1]区间的最高右木板决定,而 [right, n-1]区间的最高右木板的值已经是板上钉钉 ⇒ 自然可以计算位置right
的接水量water_right = right_max - height[right]
;既然位置right
的接水量已经确定,下一步自然是right
左移。
完整代码如下:
class Solution:
def trap(self, height: List[int]) -> int:
# 初始化结果为0
total_water = 0
# left指向数组的起始位置
left = 0
# right指向数组的末尾位置
right = len(height) - 1
# left_max记录从左到left的最大高度
left_max = 0
# right_max记录从right到数组末尾的最大高度
right_max = 0
# 当left小于right时,持续进行以下操作
while left < right:
# 更新left_max为当前left位置的高度和left_max中的较大值
left_max = max(left_max, height[left])
# 更新right_max为当前right位置的高度和right_max中的较大值
right_max = max(right_max, height[right])
if left_max < right_max:
# left_max小于right_max ==> 依据木桶原理 ==> 位置left雨水量由左木板最大高度和位置left的柱子高度共同确定
total_water += left_max - height[left]
# 向右移动left
left += 1
else:
# right_max大于等于left_max ==> 依据木桶原理 ==> 位置right的雨水量由左木板最大高度和位置left的柱子高度共同确定
total_water += right_max - height[right]
# 向左移动right
right -= 1
# 返回最终的雨水量
return total_water
运行结果:
复杂度分析
- 时间复杂度:O(N),其中 N 是数组
height
元素的数量。 - 空间复杂度:O(1)
- 利用双指针动态记录区间[0, left] 和[right, N-1]的最大值 ===> O(1)
结束语
- 亲爱的读者,感谢您花时间阅读我们的博客。我们非常重视您的反馈和意见,因此在这里鼓励您对我们的博客进行评论。
- 您的建议和看法对我们来说非常重要,这有助于我们更好地了解您的需求,并提供更高质量的内容和服务。
- 无论您是喜欢我们的博客还是对其有任何疑问或建议,我们都非常期待您的留言。让我们一起互动,共同进步!谢谢您的支持和参与!
- 我会坚持不懈地创作,并持续优化博文质量,为您提供更好的阅读体验。
- 谢谢您的阅读!