当青训营遇上码上掘金
当青训营遇上码上掘金,一共提供了四个主题,我选择的是主题4-攒青豆问题。 问题如下:(链接:juejin.cn/post/718775… )
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
下面我来说一说我的解题思路:
这道题跟经典的接雨水问题属于同一系列。我们可以把大问题拆成小问题来想,也就是先想局部问题。就比如下图中的i部分,能装下4-0=4格的青豆。用height[i]表示i的高度,那么height[i]=0,那么4-0怎么来的呢?这与左边最高的柱子lmax和右边最高的柱子rmax有关,也就是说i最高不能超过min(lmax,rmax)才能保证可以攒住青豆,而最终能攒住青豆的高度就是res[i]=min(lmax,rmax)-height[i]
方法一:暴力算法
根据这个思路可以先用暴力算法解出题目,就是嵌套for循环,这也是最容易想到的办法了。 代码如下:
但是这样时间复杂度是O(n²),空间复杂度为O(1)。还可以再来优化一下,使用备忘录方法。
方法二:备忘录法
备忘录方法就是将原来的lmax与rmax变成数组来记录最高柱子的高度。递推方程为:
lmax[i] = max(lmax[i-1], height[i])
rmax[j] = max(rmax[j+1], height[j])
代码如下:
这其实跟暴力解法大差不差,只是避免了重复的计算,时间复杂度为O(n),空间复杂度还是O(n),还可以再优化,降低空间复杂度为O(1)。
方法三:双指针法
千遍万遍,总是离不开核心思路,在这个方法里,定义指针left与右指针right,初始值left为0;right为n-1。当left==right也就是两指针相遇就可以得到攒青豆的总量了。我们可以用双指针和两个变量lmax与rmax来代替备忘录方法里的lmax[],rmax[]这两个数组。在这个方法里lmax表示左指针左边的最高柱子高度,rmax表示右指针右边的最高柱子的高度。所以得到关于lmax与rmax的递推方程:
lmax = max(lmax, height[left])
rmax = max(rmax, height[right])
想一下,若左指针高度小于右指针高度,那么lmax也肯定小于rmax,因为我们要以最低的柱子为准。所以left处能够攒青豆res = lmax - height[left],反之也是一样的道理res= rightMax - height[right]。
代码如下:
这个方法,时间复杂度为O(n),空间复杂度为O(1)。总算得到最优解法了。
通过本次主题4的攒青豆问题,我再一次回顾了备忘录法和双指针法。对于这类问题,更重要的是看局部,通过局部找到切入口。