题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 单位的青豆。
一、暴力法
我们逐个分析每个柱子所能接住的豆子,即为其左边最高的柱子长度与其右边最高的柱子长度的最小值减去该柱子本身的长度。
def catch(height):
n = len(height)
res = 0
for i in range(1, n - 1):
left = max(height[:i])
right = max(height[i:])
res += min(left, right) - height[i]
return res
暴力法的优点在于简单且易于理解,缺点在于针对每一个柱子都需要遍历一次列表,时间复杂度为O(n^2),在列表长度较大的时候运行效率会比较慢,我们是否有方法来优化这道题的解法,使程序能够仅用一次遍历便算出能接住的青豆数目呢?
二、双指针
在最左端和最右端都创建指针,并移动较矮的指针,记录沿途柱子所能承载的青豆数量,该方法的优势在于仅需要遍历一次列表。
我们知道,每个柱子所能借助的豆子数目取决于其两边最高柱子的高度以及其本身的高度。那么我们可以从两边向中间靠拢,在一次循环里面算出结果
首先我们比较最左边和最后边柱子高度,并由较矮的柱子向中心移动。因为每个柱子所能接住的豆子为其左边最高的柱子长度与其右边最高的柱子长度的最小值减去该柱子本身的长度。由此我们就可以算出沿路柱子所能借助的青豆数目。而当遇到更高柱子时,通过改变被减数来重新计算res值
def cathchV2(height):
n = len(height)
left_max = height[0]
right_max = height[n-1]
left = 0
rught = n-1
res = 0
while left < right:
if left_max < right_max:
if height[left] < left_max:
res += left_max - height[left]
else:
left_max = height[left]
left +=1
else:
if height[right] < right_max:
res += right_max - height[right]
else:
right_max = height[right]
right += 1
return res