当青训营遇上码上掘金,分享该活动中主题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 个单位的青豆。
解题思路
青豆能积攒的条件是:当柱子的高度由递减变为递增时,就一定能积攒青豆。
能积攒的青豆的数量取决于:该区间中在减至最小值前的最大值以及最小值后的最大值(即最小值的左右区间中的最大值)以及柱子的高度。所以我们对于每一个柱子(当然柱子的高度也可能是0啦)上能积攒的青豆数的算法为先求出其左右的最大值,取其中较小的值,再减去自身高度。
这是因为区间内的可以被认为均可以接满青豆,左右最大值成为一个容器的两个边界,该容器中所能存储的最大高度为左右最大值中更小的那一个(木桶效应),而由于柱子本身有高度,占据了青豆单位的空间,故每个柱子上能存储的青豆数为其左右最大值中更小的值减去其本身的高度。注意:左边界(即第一个柱子左边)和右边界(最后一个柱子右边)可认为成无限高的柱子。
代码实现
求每个柱子左边的最大值
用一个切片来记录每个柱子左边的最大值,由于要保证其最大,需不断和已记录值中的最大进行比较,故从索引1开始遍历。
n := len(height)
LeftMax := make([]int, n)
LeftMax[0] = height[0]
for i := 1; i < len(height); i++ {
LeftMax[i] = max(LeftMax[i - 1], height[i])
}
求每个柱子右边的最大值
同样用一个切片来记录每个柱子右边的最大值,由于要保证其最大,需不断和已记录值中的最大进行比较,故从原始数值中倒数第二个元素开始遍历。
RightMax := make([]int, n)
RightMax[n-1] = height[n-1]
for i := n - 2; i >= 0; i-- {
RightMax[i] = max(RightMax[i+1], height[i])
}
整体代码
注意要取左右最大值中的最小值哦~
package main
import "fmt"
func main() {
height := []int{5, 0, 2, 1, 4, 0, 1, 0, 3}
fmt.Println(trap(height))
}
func trap(height []int) int {
n := len(height)
LeftMax := make([]int, n)
LeftMax[0] = height[0]
for i := 1; i < len(height); i++ {
LeftMax[i] = max(LeftMax[i-1], height[i])
}
RightMax := make([]int, n)
RightMax[n-1] = height[n-1]
for i := n - 2; i >= 0; i-- {
RightMax[i] = max(RightMax[i+1], height[i])
}
res := 0
for i := 0; i < n; i++ {
var temp int
temp = min(LeftMax[i], RightMax[i]) - height[i]
res += temp
}
return res
}
func max(x, y int) int {
if x >= y {
return x
}
return y
}
func min(x, y int) int {
if x <= y {
return x
}
return y
}
以上内容若有不正之处,恳请您不吝指正!