当青训营遇上码上掘金————攒青豆问题
问题描述
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
实现思路
这是一道类似于Leetcode中 42.接雨水 的算法题,亦是单调栈数据结构的经典入门题目之一。
以前只用c++写算法题,这次使用Go语言实现了对攒青豆(接雨水)问题的解决。
算法的关键在于采用一个单调减栈(存放元素为heights数组对应下标),遍历柱子高度heights数组,在每一次不单调减(导致有元素出栈)时,计算一次积攒得到青豆的数目。
而这样一次计算,又可以再细分为在进行每一次“出栈”动作时分别计算、逐步统计得到的青豆数。
我们可以想象当前单调栈顶代表一个“底部”的高度,若新遍历到的height更小(不会导致有元素出栈),则入栈后自然地更新这个底部高度。由于在每一次积攒青豆时,都有一个固定而统一的“底高”,就化简了原问题中“坑坑洼洼、起起伏伏”的底部。
这样的每一次统计对应一个左端点与右端点。右端点即对应当前遍历到的height下标,左端点则是每一次元素出栈后,新的栈顶元素(一个下标)。而当栈已空时,意味着没法找到左端点了,本次积攒青豆计算也结束。
代码实现
关键代码如下:
func min(a , b int) int {
if a > b {
return b
}
return a
}
func trap(height []int) int {
var ans = 0
stack := []int{}
for i, h := range height {
for len(stack) > 0 && h > height[stack[len(stack) - 1]] {
// 每次不单调减 接一次
top := stack[len(stack) - 1]
stack = stack[:len(stack) - 1]
if len(stack) == 0 {
break
}
left := stack[len(stack) - 1]
curWidth := i - left - 1
curHeight := min(height[left], h) - height[top]
ans += curWidth * curHeight
}
stack = append(stack, i)
}
return ans
}