当青训营遇上码上掘金
阅前提示:本文是用来记录“「青训营 X 码上掘金」主题创作活动入营版”中,关于主题四:“攒青豆”的思路以及代码实现~
主题要求
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
解题思路
从题目的要求中我们可以很容易发现,接到的青豆数量是受限制于比较矮的柱子的高度。所以我们可以采用“滑动窗口”的思想来完成这一道题目(假设总共接到青豆数为total):
- 设置两个指针分别指向最左端和最右端的柱子(表示为left和right)
- 因为接到青豆数受限于矮的那根柱子,所以我们让left和right中高度低的向对方移动(假设这里是left)
- 有关left指针的移动,有两个解决思路:
- 用另一个变量temp记录left指针原本所在柱子的高度,比较temp的高度和当前left所指向柱子的高度:
1.temp >= height[left]:total+=(temp-height[left]),left继续向右移动
2.temp < height[left]: 比较当前left和right中的高度,并继续进行上述操作,直到left和right指向同一个柱子 - 用另一个指针temp记录left要移动的下一个柱子,比较temp指向柱子和left指向柱子高度:
1.height[temp] <= height[left]:total+=(height[left]-height[temp]),temp继续向右移动
2.height[temp] > height[left]: left指向柱子变成temp指向柱子(即left=temp),然后再比较当前left和right中的高度,并继续进行上述操作,直到left和right指向同一个柱子
- 用另一个变量temp记录left指针原本所在柱子的高度,比较temp的高度和当前left所指向柱子的高度:
代码呈现
以下代码为第二种实现方式:
package main
import "fmt"
func main() {
n := 0
temp := 0
fmt.Println("请输入柱子个数:")
fmt.Scanf("%d", &n)
fmt.Println("请分别输入柱子高度:")
height := make([]int, n)
for i := 0; i < n; i++ {
fmt.Scanf("%d", &temp)
height[i] = temp
}
left := 0
right := n - 1
max := 0
for left != right {
if height[left] <= height[right] {
temp = left
for temp != right {
temp = temp + 1
if height[left] < height[temp] {
break
}
max = max + height[left] - height[temp]
}
left = temp
} else {
temp = right
for temp != left {
temp = temp - 1
if height[right] < height[temp] {
break
}
max = max + height[right] - height[temp]
}
right = temp
}
}
fmt.Printf("可以接%d个单位的青豆\n", max)
}
复杂度
因为采取的解题方法需要对所有柱子高度进行遍历,所以时间复杂度:O(n)
因为要保存所有柱子的高度,所以空间复杂度:O(n)