当青训营遇上码上掘金
我在此活动中选择的是主题四:攒青豆,下面是我对此主题的理解
主题介绍
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
思路解析
看见了这个活动就想到了接雨水的问题,其实两个问题差不多,方法也一样。方法如下
方法1(按列算)
- 这也是最容易理解的一种方法,我们计算每一个柱子上方的青豆最多有多高就行了,而这个高度取决于它的左右两边最高的柱子分别是多高。
- 也可以求左右两端最高的高度,不过需要预处理一下,用数组保存一下每个位置左右两端最高的高度就行了。
- 最后就是用左右两边最高高度的较小值,减去这根柱子的高度。
- 时间复杂度 ,空间复杂度 ,需要扫描两遍数组。
方法2 双指针法
左右指针向中间移动。左指针是左边柱子最大高度,右指针是右边柱子最大高度。当左指针小于右指针时,左指针右移;当左指针大于等于右指针时,右指针左移。
时间复杂度:O(N)。
空间复杂度:O(1)。
方法思路:
- 先定义左右两个柱子和总青豆数,能攒多少青豆就看左右两个柱子高度
- 再定义第二个和倒数第二个的两个柱子,让柱子分情况向中间遍历
- 如果左边柱子小于等于右边柱子,青豆数增加,根据左边柱子和第二个柱子比较最大值,更新为左边的柱子
- 如果左边柱子大于右边柱子,青豆数增加,根据右边柱子和倒数第二个柱子比较最大值,更新为右边的柱子
- 当左右两个指针为同一位置时,结束循环。得最终青豆数
代码实现
代码实现采用方式二双指针形式,代码如下:
package main
import "fmt"
func main() {
height := []int{5, 0, 2, 1, 4, 0, 1, 0, 3}
ret := trap(height)
fmt.Println(ret)
}
func trap(height []int) int {
N := len(height)
if N <= 2 {
return 0
}
left := height[0]
right := height[N-1]
L := 1
R := N - 2
count := 0
for L <= R {
if left < right {
count += getMax(getMin(left, right)-height[L], 0)
left = getMax(left, height[L])
L++
} else {
count += getMax(getMin(left, right)-height[R], 0)
right = getMax(right, height[R])
R--
}
}
return count
}
func getMax(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
func getMin(a int, b int) int {
if a < b {
return a
} else {
return b
}
}