当青训营遇上码上掘金
题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
分析
这是非常经典的接雨水问题,力扣42,不过变成了接青豆:)
这道题有多种解法,常见的是暴力、双指针、动态规划和单调栈,下面从栈的角度来分析解决一下这个问题。
栈的解法类比于括号匹配,每次匹配出一对括号(即找到匹配的两堵墙),再计算这两堵墙中的能攒的青豆数量。
整体思路就是用栈来保存墙的下标,遍历高度,若当前墙的高度大于栈顶的高度,出栈,计算出这两墙之间的青豆量并累加,循环比较当前墙的高度和新栈的栈顶高度直至当前墙的高度小于等于栈顶的高度或者是栈空了就退出循环;否则若当前墙高度小于等于栈顶的高度,就把当前墙入栈,指针后移一位,继续遍历。
至于计算当前指向墙和新的栈顶墙之间的青豆数量,参考水桶效应,两墙较矮的减去旧栈顶的高度,青豆数就是 Min ( max _ left ,max _ right ) - height [ top],最后乘上两个墙之间的距离。
核心代码
go语言实现如下
func trap(height []int) int {
stack := make([]int, 1, len(height)) // st存储的是高度数组下标
sum := 0
current := 0
for current < len(height) {
//若栈不空且当前高度大于栈顶高度
for len(stack) != 0 && height[current] > height[stack[len(stack)-1]] {
top := stack[len(stack)-1] //出栈的元素
stack = stack[:len(stack)-1] //出栈
//判断当前栈是否为空
if len(stack) != 0 {
distance := current - stack[len(stack)-1] - 1 //墙间距
tmp := min(height[current], height[stack[len(stack)-1]]) - height[top] //水量
sum = sum + distance*tmp
}
}
stack = append(stack, current) //当前的墙入栈
current++ //指针移动
}
return sum
}