当青训营遇上码上掘金
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
*解题思路:
由图可得,青豆是在左右两端柱子之间的空隙攒下来的,不同的柱子高度能积攒不同数量的青豆,在由两端柱子隔绝的每一堆青豆中,我们发现:两端的柱子分别是这一堆柱子中最高的和次高的柱子。
根据木桶效应可知:每个木桶能装的水的高度取决于最短的木板,即以最短的木板高度为基准。所以在攒青豆中,我们可以很容易地想到,一排柱子的左右两端,需要以较短的一端为基准高度。
那么这个问题可以转化为:如何在一排柱子中找到一堆堆的青豆,即如何在一排柱子中找到几组特殊的“端点柱子”。
所以我们需要从最左、最右两端的柱子同时开始,比较它们的长短,注意,这里首先有个坑:如果较短的一端的柱子长度为0,那么它无法攒下青豆,以它为基准就没有任何意义,故我们需要将长度为0的某一端柱子“剔除”。
在示例中我们可以看到,最右端的柱子比最左端的柱子短,那么我们首先以最右端柱子往左看,一直找到一个高度为4的柱子比最右端柱子高(或等高),于是我们找到了第一个青豆堆,计算其青豆数量,再将较高的柱子标记为最右端;然后我们继续找下一个青豆堆,一直找到一个高度为5的柱子比最右端柱子高(或等高),于是我们找到了第二个青豆堆,计算其青豆数量。再将较高的柱子标记为最右端,由于此时最左端和最右端都是同一个柱子,表示青豆已经攒完了,此题得解~
至于每一堆的青豆数量要怎么计算,要知道我们在找青豆堆的时候已经找到了其中一端的柱子,那么中间的柱子若有高度,则该柱子的高度为阻碍青豆的积攒数,将中间柱子的高度累加,直到找到该青豆堆的另一端柱子,两端柱子以较短的一端为青豆堆的高度,青豆堆的宽度为两端柱子的距离,相乘后再减去中间柱子的高度和,即为该青豆堆的数量。
本人的代码:
function gatherBeans(arr) { let sum = 0; let left = 0; let right = arr.length - 1; while(left < right) { if(arr[left] > arr[right]) { if(arr[right] == 0) { right--; continue; } for(let i = right - 1; i >= left; i--) { if(arr[i] < arr[right]) { sum -= arr[i]; } else { sum += arr[right] * (right - i - 1); right = i; break; } } } else{ if(arr[left] == 0) { left++; continue; } for(let i = left + 1; i <= right; i++) { if(arr[i] < arr[left]) { sum -= arr[i]; } else { sum += arr[left] * (i - left - 1); left = i; break; } } } } return sum;}