当青训营遇上码上掘金
主题介绍
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
解决方案
思路分析
对于下标i处能接到的青豆多少,取决i两侧最高的柱子(分别记为leftMax,rightMax)中小的那一个的高度:青豆数= min(leftMax, rightMax) - height[i]。
方案一
双数组实现动态规划
-
创建两个长度为n的数组,分别记录下标i处的leftMax和rightMax,leftMax正向遍历可得,rightLeft逆向遍历可得。
-
得到上述数组后遍历每个下标位置计算接到的青豆数获得青豆总量。
方案二
双指针
双指针的实现思路与双数组总体思路相近,但相比于双数组降低了空间复杂度:O(n) -> O(1)
-
维护两个指针left和right,以及两个变量leftMax和rightMax:left、leftMax以及leftRight的初始值为0,right初始值为n-1。
-
两指针没有相遇时:取left和right下标位置中较小的数与leftMax和rightMax计算( min(leftMax, rightMax) - height[i]),并向前(left)/向后(right)移动。
-
当两指针位于同一位置时结束循环。
关键代码
笔者主要双指针形式实现,关键代码如下:
func QingdouCalculate(height []int) int {
left, right := 0, len(height)-1
leftMax, rightMax, res := 0, 0, 0
for left < right {
//更新leftMax和rightMax
leftMax = max(leftMax, height[left])
rightMax = max(rightMax, height[right])
if height[left] < height[right] {
res += leftMax - height[left]
left++
} else {
res += rightMax - height[right]
right--
}
}
return res
}
func max(x int, y int) int {
if x > y {
return x
}
return y
}
func min(x int, y int) int {
if x < y {
return x
}
return y
}