「青训营 X 码上掘金」主题创作活动——主题 4:攒青豆(go语言)

102 阅读3分钟

当青训营遇上码上掘金

本文简单介绍下 青训营 X 码上掘金活动主题 4 “攒青豆” 的几种解法:

  1. 最形象的暴力解法
  2. 优化后的解法
  3. 双指针解法
  4. 单调栈解法

题目描述:

现有 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 个单位的青豆。

核心思路

看图片中的第二列是存在4个单位的青豆的,这是怎么样得来的呢。是这一列的左边最高的柱子右边最高的柱子(包括自己),并取两者之间最矮的,然后减去自身柱子的高度就可以得到了,如第二列就是:

第二列青豆单位 = min(max(左边的所有柱子), max(右边的所有柱子)) - 自身柱子高度

这是最形象的解决思路

根据此我们可以写出以下函数:

image.png

这是最简单的一种解法。其时间复杂度很容易看出是O(n2n^2)

优化暴力解法(动态规划)

从以上代码中我们可以看出,在每个for循环里面都要从头去计算左右的最大值,那么我们可以使用数组记录一下每个位置左右的最大值,从而减少循环次数从而进行优化。

代码如下所示:

image.png

将上述代码的时间复杂度由O(n2n^2)优化到了O(nn)。

双指针解法

我们可以从上面的代码中发现分别有两个指针从左右出发,那么我们就可以使用双指针的方法来进行求解。 指针分别从左右起点出发。 l_max 是 height[0..left] 中最高柱子的高度,r_max 是 height[right..end] 的最高柱子的高度 这个for循环里面的if判断是比较难想到,就是反正值左右两边最高的取最小的一侧那么我只算一侧的话只要比目前左边某个位置的最高的低就可以进行计算了。

image.png

单调栈解法

维护一个单调栈,单调栈存储的是下标,满足从栈底到栈顶的下标对应的数组 height 中的元素递减。 从左到右遍历数组,如果当前栈顶元素小于将要入栈的元素就开始弹出找到比入栈元素大的再入栈。被弹出的元素下面的元素就是可以接住青豆的凹槽,计算两者之间的面积再减去自身的高度就得到结果了。

image.png

最后是代码链接: