接雨水?攒青豆!|「青训营 X 码上掘金」主题创作

35 阅读3分钟

当青训营遇上码上掘金
接什么雨水,感觉不如青豆....含金量..

题目

主题4:攒青豆

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

activity-jieyushuii.jpg

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

分析

  • 从宏观上分析:我们可以发现想接住雨水青豆必须有个“凹”的形状,或者说,每当有个“凹”就可以计算雨水堆积了,那我们可以联想到什么?括号匹配! 而提到括号匹配,需要用一个经典的数据结构,也就是栈。
  • 从微观上分析:在本题,我们会用一个特殊维护的栈:单调栈 ,之所以用这个,是因为每次出现比前一个高的柱子的时候我们就需要计算其能接的青豆数,如图中0 和 2 , 1 和 4 , 1 和 0等。
    当然,细心一点就会发现,我们不仅需要对比前一个,还需要和更前一个进行比较高低,以的那个为基准统计青豆数.

具体代码实现


我们就以题目的例子来说明:

  • 首先我们将 height[0] , height[1] 入栈,当 遍历指针(下面简称Cur)指向 height[2] 时,发现其比栈顶的值大,于是进入循环进行计算.
  • 循环1: 将栈顶弹出,记录为 top_num,计算如今cur的位置与栈顶的距离 dis,并比较大小 minum ,通过 dis * (minum - top_num) 获得青豆数 2,计入 res 中,cur++,继续判断height[3]与当前栈顶的大小,发现满足单调栈,压入栈中.跳出循环,cur++ qindou-1.png
此时 stack = {5,2}
  • cur指向 height[4],发现比栈顶大,进入循环进行计算
  • 循环2: 同之前一样,取出栈顶记录为 top_num,计算 height[4]height[2] 距离 dis,比较大小获得 minum,计算青豆数 1,此时继续判断 height[4] 与 当前栈顶height[2]大小,发现仍然大于栈顶,循环继续;将栈顶取出,计算 height[4] 与 栈顶 height[0](注意之前的height[1]已经被弹出) 距离 dis,比较大小获得 minum ,计算获得青豆数 6,此时height[4] 小于栈顶,跳出循环,cur++. qindou-2.png
此时 stack = {5,4}
  • cur指向 height[5] 压入栈中,随后 cur++ 此时 cur所指柱子大于栈顶,进人循环进行计算
  • 循环3: 取出栈顶,统计height[6]与当前栈顶(height[4])距离 dis,比较大小获得 minum,计算获得青豆数 1cur++,判断发现栈顶已经比cur所指柱子大,将height[6]压入栈中,跳出循环,cur++ qindou-3.png
此时 stack = {5,4,1}
  • cur指向height[7],发现比栈顶小,压入栈中,cur++,发现cur指向的height[8]比栈顶大,进入循环
  • 循环4: 取出栈顶,计算获得青豆数 1,继续判断会发现height[8]仍然比栈顶大,继续取出栈顶,计算获得青豆数 6qindou-4.png
此时 stack = {5,4,3}
  • 至此 一共获得 青豆数 2 + 1 + 6 + 1 + 1 + 6 = 17 和解析答案一致.

总结

这道题可以说是经典困难题了,有按行求,按列求,双指针,动态规划等等很多解法,本文讲的是经典的单调栈解法,理解思路后结合图能很直观的意识到单调栈的作用。leetcode也有很多优秀题解.