当青训营遇上码上掘金
主题介绍
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
方法一:动态规划
我们可以看到只要记录左边柱子的最高高度和右边柱子的最高高度,就可以计算当前位置的青豆面积,这就是通过列来计算。当前列青豆面积:min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。为了得到两边的最高高度,使用了双指针来遍历,每到一个柱子都向两边遍历一遍,这其实是有重复计算的。我们把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight)。这样就避免了重复计算,这就用到了动态规划。当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。即从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i-1]);从右向左遍历:maxRight[i] = max(height[i], maxRight[i+1]);这样就找到递推公式。
方法二:双指针
动态规划的做法中,需要维护两个数组leftMax和rightMax,因此空间复杂度是O(n)。是否可以将空间复杂度降到0(1)? 注意到下标i处能接的青豆量由leftMax[i] 和rightMax[i]中的最小值决定。由于数组leftMax是从左往右计 算,数组rightMax是从右往左计算,因此可以使用双指针和两个变量代替两个数组。 维护两个指针left和 right, 以及两个变量leftMax和rightMax, 初始时left= 0, right = n - 1, leftMax = 0, rightMax= 0。指针left 只会向右移动,指针right只会向左移动,在移动指针的过程中维护两个变量 leftMax和rightMax的值。 当两个指针没有相遇时,进行如下操作:
- 使用height[left和height(right的值更新leftMax 和rightMax的值;
- 如果height[left < height[right,则必有leftMax < rightMax,下 标left 处能接的青豆量等于leftMax一 heightl[eft,将下标left处能接的青豆量加到能接的青豆总量,然后将left 加1 (即向右移动位);
- 如果height[left ≥height[right,,则必有leftMax ≥rightMax,下 标right 处能接的青豆量等于 rightMax - height[right,将下标right 处能接的青豆量加到能接的青豆总量,然后将right 减1 (即向 左移动位)。
当两个指针相遇时,即可得到能接的青豆总量。