主题 4:攒青豆
题目:
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
用例:
输入:height = [5,0,2,1,4,0,1,0,3] 输出:17 解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
自己的分析:
该题其实刷算法的小伙伴一眼就能看出这是一道披着《接雨水》外套的攒青豆问题hhh,然后这题是非常经典的单调栈的题,而为什么要使用单调栈呢,因为装豆子的高度取决于柱子两边的较低一边的高于与凹槽的高度差,而宽度是取决于左右柱子的下标差。
使用单调递减的栈去操作的话,
当我们遇到元素小于栈顶元素则将该元素下标放入栈中
当等于栈顶元素应弹出栈顶元素再将遇到的元素放入栈中以更新下标
当我们遇到了比栈顶元素更大的元素,此时就可以装青豆,因为栈顶元素左右两边柱子都更高,
而栈顶元素相当于就是一个凹槽,而单调栈存放的是元素的索引,因此宽度就可以使用栈元素进行解决,而高度即为左右元素较小的一个减去栈顶这个凹槽高度。
依次循环计算每个栈顶元素比当前元素小的凹槽空间,并将栈顶元素弹出,直至栈顶元素大于当前元素,并将当前元素压入栈顶
以上就是整个解题思路。
解题代码(Python)
def main() -> None:
# 单调栈
height =[5,0,2,1,4,0,1,0,3]
stack = [0] # 首先把第一个元素下标放入栈中
result = 0
for i in range(1, len(height)):
if height[i] < height[stack[-1]]: #元素小于栈顶元素则放入栈中
stack.append(i)
elif height[i] == height[stack[-1]]: # 当等于栈顶元素需要更新栈顶元素下标
stack.pop()
stack.append(i)
else:
while stack and height[i] > height[stack[-1]]: # 当遍历元素大于栈顶元素则
# 栈顶就是中间的柱子:储槽,就是凹槽的底部
mid_height = height[stack[-1]]
stack.pop()
if stack:
right_height = height[i]
left_height = height[stack[-1]]
h = min(right_height, left_height) - mid_height
w = i - stack[-1] - 1
result += h * w
stack.append(i)
print(result)
if __name__ == '__main__':
main()
感想
对于单调栈解决类似题目还有像leetcode中(84. 柱状图中最大的矩形 - 力扣(Leetcode)),像单调栈这种方法或者其他并查集之类的大部分其实都是模板题,几乎都是同样的模板去套,但是要熟悉吃透为什么是这么一个模板,这样才能更好的记住,以后使用起来才不会“好像是用这个方法来着,但是不会写”这样的情况。