当青训营遇上码上掘金
主题描述
我选择的主题为攒青豆,主题描述如下:现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
复制代码
题目分析
看到本题目描述,想必很多人都想到了leetcode上的经典题目lc42接雨水,二者思路基本上是相同的。我们可以将整体划分开来看,只要局部形成一个不规则的“凹”字形就可以存储青豆。这个“凹”字的宽度无上限,底部也不需要是一马平川的,左右也不需要同样大小。总地来看,只要局部出现右边高于中间且宽度大于等于3便可以以矩形形状攒下青豆,攒青豆的最高上限为左边和右边中较短的那个边,这样的特性让我们自然想到了单调栈。单调栈是一种单调递增或单调减的栈,跟单调队列差不多,但是只用到它的一端,在这里我们可以使用一种单调减少的栈来存储这些柱子的信息,下面我用java语言编程实现一下攒青豆的过程。
代码实现
实现思想
首先思考一下极端情况:如果柱子个数小于等于两个,那就根本没有攒下青豆的可能,直接输出0即可。
若柱子个数大于2个,便有了攒下青豆的可能,在开始攒之前先做一些准备工作:创建青豆总数变量sum、创建单调栈stack,这里用LinkedList实现的Deque代替。在这里单调栈存储的是各个柱子的索引数,而非柱子的高度数,这是为了避免出现相同高度的柱子处理错误的现象。
在向单调栈中存入起始柱子索引数0后我们便可以开始我们的攒青豆过程。从索引1开始挨个遍历柱子的高度。
- 若当前柱子高度小于栈顶柱子高度,想象一下,青豆会顺着这个阶梯滑落,无法储存,所以将当前柱子入栈,等候更高的柱子来临。
- 若当前柱子高度等于栈顶柱子高度,则可柱顶栈顶元素出栈,替换为当前柱子索引信息,其实这样的替换不是必要的,但进行这样的替换之后可以减少总的处理次数。
- 若当前柱子高度大于栈顶柱子高度,则我们可以将栈顶元素出栈,将其所索引的柱子高度当作“凹”字的底,再由于单调递减栈的性质,其前面的元素所索引的高度均大于此栈顶元素,所以一定可以攒下青豆,然后将新栈顶元素所索引的高度和当前柱子高度中的最小值和已出栈元素高度的差值作为攒青豆的上限h,将当前柱子索引和新栈顶元素相差的距离作为攒青豆的宽度w,一旦这个h乘以w的值大于0,便可以加入到青豆总数变量sum中。不断重复这个过程,直到当前柱子高度小于等于栈顶元素高度才停止,将当前索引入栈,结束本次过程。
当所有柱子都处理过后,直接输出sum即可。