当青训营遇上码上掘金
主题 4:攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
代码逻辑
双指针法
用双指针法纵向统计青豆。按照纵向来计算的话,每列青豆的宽度一定是1,将每一列的青豆高度求出来相乘便得到了每一列上的青豆数量。通过分析可以看出每一列青豆的高度,取决于该列左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度,将这个柱子的高度与当前列的柱子高度相减便得到了当前列上可以容纳的青豆数量。以上述图的情况为例,高度为2的柱子左侧最大高度的柱子的高度为5,右侧最高高度的柱子高度为4,所以高度为2的柱子所能承载的豆子数量为min(5,4) - 2,这样依次遍历所有柱子,每个柱子通过双指针求左侧和右侧最大值即可。因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2)。 空间复杂度为O(1)。本题也可以采用动态规划或者利用单调栈进行实现,但是双指针法是比较符合第一次做题的直觉思维,简单易懂。
动态规划法
当前列青豆度,记录右边柱子的最高高度) - 当前柱子高度。 为了得到两边的最高高度,使用了双指针来遍历,每到一个柱子都向两边遍历一遍,这其实是有重复计算的。我们把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight)。这样就避免了重复计算,这就用到了动态规划。当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。 从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]); 从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);