当青训营遇上码上掘金
本篇文章归档于 “第五届字节跳动青训营”,主要是为了完成和记录掘金的 “青训营 x 码上掘金” 活动,如果你对我的其他文章感兴趣,可以去我的 专栏 中逛逛看有没有你想要的东西。
后端营推荐了两个可供选择的题目:寻友之旅和接青豆。
“寻友之旅” 是一道非常经典的动态规划,感觉并没有太多可扩展的空间,于是我打算简单说说 “接青豆” 的解题思路。
什么是 “接青豆”
先附上活动给的题目描述:
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
再来张官方的示意图,或许会更清晰些: (水篇幅嫌疑,嘿嘿!;)
有经常刷题的同学可能已经反应过来了,这其实是一道 hard 的 “接雨水”,如果你想立刻跑去 coding,这里顺手给个通道 接雨水(习惯英文版的看这里 Trapping rain water
如何 “接青豆”
简单来说,无论是 “接雨水”,还是 “接青豆”,实际上就是水桶效应,桶内的容量与木板的长度有关,并由最短的木板来定。
方法一:比较最长板
我们可以维护两个数组:toHead, toTail。记录每个状态下的最高板长,区别在于 toHead 是从头向尾比较,toTail 反之。
// n is length of array
n := len(height)
// toHead store the heightest pillar from current to the head of array, toTail is opposite
toHead, toTail := make([]int, n), make([]int, n)
for max, i := 0, 0; i < n; i++ {
if max < height[i] {
max = height[i]
}
toHead[i] = max
}
for max, i := 0, n - 1; i >= 0; i-- {
if max < height[i] {
max = height[i]
}
toTail[i] = max
}
通过遍历两次数组,我们可以拿到每个状态下,相较于两端的最长板。此时,每个状态能承载的容量就是较小的那个“最长板”。最后,再遍历一次,统计每个状态下的容量即可。
// count beans
for i := 0; i < n; i++ {
// lower one between toHead and toTail is top of the beens
top := min(toHead[i], toTail[i])
result += top - height[i]
}
方案二:比较边缘板
方案一的思路很符合人类的思考模式,也非常容易理解。
美中不足在于它需要遍历三遍数组,并且需要维护两个记录状态的数组。当原始数据非常大时,它需要再开辟两个同样大小的空间,并便利 3 次。
因此,我们可以尝试寻找一个更高效的解决方案。先来看这样一个场景:假设桶内有多个隔板做隔断,那桶的实际容量应该有多少?
- 如果隔板高于边缘板:那边缘板就是最短板,从边缘到隔板的容量都是边缘板
- 如果隔板低于边缘板:那较短的边缘板就是最短板,隔板的高度仅在于减少了容量,容量依旧是较短的边缘板。
现在,你是否已经想到了更好的解决方案了呢?
这是一条 break line:可以短暂停留,希望你能简单思考一下~
我们需要两个值:max_left,max_right。max_left 是左边缘板的长度,max_right 同理。为什么前缀加了一个 max?因为我们需要维护边缘板的长度。
然后,让两端的边缘板不断逼近,移动较短边缘板的与此同时记录每个状态的容量即可,直到相遇:
// max_left is the biggest value from left, max_right as well
max_left, max_right := 0, 0
// head & tail are the index of array
head, tail := 0, len(height) - 1
// iterate over the array
for head <= tail {
if max_left <= max_right {
if max_left < height[head] {
max_left = height[head]
} else {
result += max_left - height[head]
}
head++
} else {
if max_right < height[tail] {
max_right = height[tail]
} else {
result += max_right - height[tail]
}
tail--
}
}
最后放一点想说的话
文章是在初一写的,先祝看到这里的读者:新年快乐~
方案二相较于方案一可能会难理解一些,不过还是值得仔细回味,如果有什么问题,欢迎评论区留言。