当青训营遇上码上掘金--攒青豆篇
当青训营遇上码上掘金--攒青豆篇。 此题中计算青豆的单位数目,很容易就可以想到实际上是转化成高度的比较和差值问题。我们所要解决的问题有两个:(1)在给定了每个柱子高度的基础之上,如何表示第n列可以存放的青豆数目;(2)已知每列可以存放的豆子数目,如何计算所有列可以存放的豆子总数目。很显然第一个问题是第一个问题的基础,
如何表示第n列存放的青豆?
以此图为例,我们可以轻松知道,最左和最右一列柱子始终是无法存放豆子的,那么存放豆子的列下标就是从1开始。我们把当前列的左侧未遍历的最高柱子称为左端点,相应的右侧未遍历的最高柱子称为右端点。
按从左向右的顺序来看,右侧最高的柱子高度为4,右端点的高度是要低于左端点高度为5。那么在height=4之前,所有的柱子的最高高度都不可能超过4,每列能够存放的青豆数目都等于 height - 自身高度。当然也存在另外一种情况就是右侧找到高度最高的柱子比左端点高度还要高,那么此时这个height就要等于左端点柱子的高度。
综上所述,第n列存放的豆子数目 = 左右两端点中较低的柱子高度 - 第n列自身高度。
由解决方案中提出了左右端点可知,我们可以使用双指针的方式,来遍历柱子高度,并进行累加。快指针right向前查找右端点,慢指针left停留在上一次查找的最高柱子的下一列,那么就可以始终保证左端点(left-1)与右端点(right)是分别指向左右最高的柱子。我们也可以直观的认识到,一旦某一列柱子被作为端点,那么就一定无法存放青豆。
//若右侧最高一列比当前左端点低,则以右端点为基准
if height[right] <= height[left-1] {
//那么就计算当前位置至右侧最高一列的豆子数目
for _, v := range height[left:right] {
//两侧较低的一侧高度 - 自身高度
sum += height[right] - v
}
} else { //若右侧最高一列比当前左端点还高,则以左端点为基准
for _, v := range height[left:right] {
sum += height[left-1] - v
}
}
如何表示所有列存放的总青豆?
毫无疑问,我们要累加每列存放的青豆,这里查找右侧最高柱子的方法就不再赘述,利用数组或切片的下标关系,从left位置开始查找即可。
采用循环的方式,开始对height数组或切片进行遍历,每次循环找到一个right后,就计算在 left -right-1 之间所能存放的青豆数目并累加,计算完成后,注意要将左指针left右移至right+1的位置,因为right的位置时下一次查找的左端点。当左指针left移动到最后一个下标的位置时,则表示全部数组或切片遍历完成,计算也完成,退出循环,输出结果。
完整代码
package main
/*
Author: 丶Lumi
Date:2023/1/13 13:02
IDE:GoLand
Project:src
*/
import "fmt"
func MaxHeight(height []int, index int) int {
maxHeight := 0
for i, v := range height {
//必须是找当前位置右侧更高的一列
//所有高度要最高,位置还要靠右
if v > maxHeight && i > index {
//此处找最大值不可取等,高度相同的两列应分开计算
maxHeight = v
index = i
}
}
return index
}
func main() {
var height = []int{5, 0, 2, 1, 4, 0, 1, 0, 3}
//格式化输出高度
for i, v := range height {
if i == 0 {
fmt.Printf("height = [%d ", v)
} else if i == len(height)-1 {
fmt.Printf("%d]", v)
break
} else {
fmt.Printf("%d ", v)
}
}
//首先明确一点:在最左与最右的两列一定是无法存放豆子的
var sum = 0
//第n列存放豆子数目为 以自身为基准(左右两侧较低的一侧高度 - 自身高度)
//采用双指针法,从左向右遍历
//左指针指向能存放豆子的列,开始位置一定为第一列
left, right := 1, 0
//右指针指向当前位置右侧最高的列,
for {
//从当前位置开始,找到右侧最高的一列
right = MaxHeight(height, left)
//若右侧最高一列比当前左端点低,则以右端点为基准
if height[right] <= height[left-1] {
//那么就计算当前位置至右侧最高一列的豆子数目
for _, v := range height[left:right] {
//两侧较低的一侧高度 - 自身高度
sum += height[right] - v
}
} else { //若右侧最高一列比当前左端点还高,则以左端点为基准
for _, v := range height[left:right] {
sum += height[left-1] - v
}
}
left = right + 1
//当左指针指向最右侧时,计算完成,退出循环
if left == len(height) {
break
}
}
fmt.Printf("\n%d\n", sum)
}
测试样例: