当青训营遇上码上掘金
参加「青训营 X 码上掘金」主题创作活动,我选择了主题四,因为我自己擅长的是后端方向。
主题四是对应了一个很经典的接雨水问题。
这道题目有多种解题方案,比如:
- 按照行来计算青豆数
- 按照列来计算青豆数
- 还有使用动态规划方法来实现
- 还有使用单调栈来实现
从思路上来说: 我们对于下标 i,下雨后水能到达的最大高度等于下标 i 两边的最大高度的最小值,下标 i 处能接的雨水量等于下标 i 处的水能到达的最大高度减去 height[i] 那么如何高效地计算出下标i 两边的最大高度的最小值 成为了这题的重点,如果我们采用暴力遍历的方法,那么时间复杂度将会达到, 因为对于每一个i,都需要左右遍历找。这并不是一个好的方案。
动态规划,按照列求答案:
我们可以维护两个最大高度的数组leftmax,rightmax。
. 对于其他的值,我们采用以下递推公式:
然后我们就能够根据 累计起来,就是我们要的答案了。
单调栈:
维护一个单调栈,单调栈存储的是下标,满足从栈底到栈顶的下标对应的数组 height 中的元素递减。
单调栈实际上就是去找到了一个凹陷左边的部分,即对于坐标 i,i左边能够形成一个凹陷来存储青豆(起码两个) 能够入栈的原则是height[i] < heght[stack[-1]]. 如果不小于,则说明存在一个存储青豆的区域了。 那么高度就是 min(heght[stack[-1]](左), heght[i](右)) - heght[stack[-1]] * i - left - 1 (宽度)
最终实现代码为:
from typing import List
def main(height: List[int]) -> None:
# 使用单调栈
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]
w = i - left - 1
currHeight = min(height[left], height[i]) - height[top]
ans += w * currHeight
stack.append(i)
return ans
if __name__ == '__main__':
print(main([5,0,2,1,4,0,1,0,3]))