攒青豆|「青训营X码上掘金」主题创作

30 阅读2分钟

当青训营遇上码上掘金

主题介绍

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

image.png

以下为上图例子的解析:

输入: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]。

方案一

双数组实现动态规划

  1. 创建两个长度为n的数组,分别记录下标i处的leftMax和rightMax,leftMax正向遍历可得,rightLeft逆向遍历可得。

  2. 得到上述数组后遍历每个下标位置计算接到的青豆数获得青豆总量。

方案二

双指针

双指针的实现思路与双数组总体思路相近,但相比于双数组降低了空间复杂度:O(n) -> O(1)

  1. 维护两个指针left和right,以及两个变量leftMax和rightMax:left、leftMax以及leftRight的初始值为0,right初始值为n-1。

  2. 两指针没有相遇时:取left和right下标位置中较小的数与leftMax和rightMax计算( min(leftMax, rightMax) - height[i]),并向前(left)/向后(right)移动。

  3. 当两指针位于同一位置时结束循环。

关键代码

笔者主要双指针形式实现,关键代码如下:

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
}