主题 4:攒青豆

75 阅读3分钟

当青训营遇上码上掘金

题目

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

攒青豆.png

以下为上图例子的解析: 输入:height = [5,0,2,1,4,0,1,0,3] 输出:17 解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

思路

我们可以把上给的测试用例中的图看做是一个一个单位的网格。每一个单位的网格能不能放入青豆,取决于它左右是否有隔档。 比如,绿色的格子就可以放青豆,因为他的左右都有隔档;红色的格子就无法放青豆,因为它右侧没有隔档,青豆会从右侧流走。

流程图 (27).jpg

因此,我们就可以统计每个位置有多少个格子能放青豆就可以。以1位置为例,可以放四个青豆,因为再向上增长的格子右侧没有隔档。于是,不难发现,每个位置能最高青豆能放放到哪个位置就是左边隔档最高值与右侧隔档最高值的较小者(木桶原理)。

流程图 (28).jpg

知道了i位置青豆能放的最高位置,再减去i位置隔档的高度就是该位置的青豆单位数。

于是一个简单暴力的想法便产生了:来到每个位置,我都统计出左侧的最大值和右侧的最大值即可。这种操作的时间复杂度是O(n)O(n),总体的时间复杂度就是O(n2)O(n^2)。那么有没有复杂度小一点的呢?当然有,我们可以 把获取左右最大值的时间复杂度降到O(1)O(1)。当然了,需要通过空间换取时间。操作如下:

以统计左侧最大值为例,我们准备一个数组leftMax

流程图 (30).jpg

  • 对于0位置,左侧没有值,我们可以把左侧的最大值记为0,填入leftMax数组中

流程图 (31).jpg

  • 对于1位置,我们用height[0]leftMax[0]比较,选择较大值。这样在统计1位置左侧的最大值时,直接从leftMax[1]中拿就可以。

流程图 (32).jpg

  • 对于2位置及其后续的位置,同样的道理,我们就直接填完整leftMax数组。

流程图 (33).jpg

同理,我们可以获得右侧最大值的数组rightMax

流程图 (35).jpg

然后根据准备好的leftMaxrightMaxheight就可计算每个位置的青豆了。0位置和最后一个位置是不用统计的,因为左右两侧没有隔档。

  • 1位置:左侧最大值5,右侧最大值4,两侧较小值是0,该位置隔档高度0,获得青豆单位数是4;
  • 2位置:左侧最大值5,右侧最大值4,两侧较小值是0,该位置隔档高度2,获得青豆单位数是2;
  • 3位置:左侧最大值5,右侧最大值4,两侧较小值是0,该位置隔档高度1,获得青豆单位数是3;
  • 4位置:左侧最大值5,右侧最大值3,两侧较小值是3,该位置隔档高度4,右侧没有隔档,获得青豆单位数是3;
  • 5位置:左侧最大值5,右侧最大值3,两侧较小值是3,该位置隔档高度0,获得青豆单位数是3;
  • 6位置:左侧最大值5,右侧最大值3,两侧较小值是3,该位置隔档高度1,获得青豆单位数是2;
  • 7位置:左侧最大值5,右侧最大值3,两侧较小值是3,该位置隔档高度0,获得青豆单位数是3;

青豆单位数加起来,总和是17。

代码