主题4:攒青豆—Java单调队列(简单易懂)

100 阅读2分钟

当青训营遇上码上掘金

本次活动,选择的题目为主题4:攒青豆。不同于题目三的一眼背包问题,动态规划。这道题目比较容易想出有意思的其他解法。且相比单调栈,动态规划,意义更明确,清晰易懂

面对算法题,一般分四步走

  1. 问题分析:抽象场景问题为数学问题
  2. 算法:寻找适合的算法思路
  3. 分析复杂度:分析解法的时间复杂度,空间复杂度
  4. 编程实现

问题分析

原题:

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

抽象

分析该题目,接住青豆多少,取决于凹槽容积多大。

形成的凹槽容积,是短边×底-槽底,题目就可转换为,如何便捷的获取不同的凹槽的边。或者说,如何确定当前索引能达到的青豆高度。如何求 目标数组(填满青豆)与当前数组值差

算法

结合原题目的图,我们不难发现,对于最大值在数组边上的,从另一头开始的单调不减序列,能分级的判断容量。同样的,最大值在数组中间的,就从两边的单调序列,注意边界值即可。

所以很容易得出一种获得短边的方法如下:

  1. 从数组起始,求其单调不减序列,存储其位置
  2. 对该序列,除该序列最后一个值外,当前的最大值都决定了之后的豆子高度
  3. 将能达到的高度与该位置值相减,累加得到序列末尾的青豆
  4. 若数组末尾值小于序列末尾值(即最大值位于中间:不可能大于,等于即位于末尾),即可以单调递增的方式直接遍历。

复杂度

最多遍历2n,时间复杂度为O(n)。需要构建单调队列,空间复杂度为O(n)

编程实现

使用java的队列,来构造单调队列。代码如下