解题思路:
基本思路:
本题可以用木桶原理的思路,每个柱子上所能接到的水量的取决于其左右两侧的最高柱子中最小的一个。解释来说就是某根柱子a所接的水可以看作是一个木桶装水,柱子a为木桶的底板,a左边的最高柱子a_left_max为木桶的左挡板,a右边的最高柱子a_right_max为木桶的右挡板。这个木桶完全由这三根柱子所决定。
思路到这里题目就已经可以解决了,只要遍历一下数组,找到每个高度左右两边最大的高度,然后再一根一根柱子算一下所接的水量,最后求个和题目得解。
这种思路的时间复杂度为O(n),空间复杂度也为O(n),因为要存储每个高度的左右两边最大高度这个数组。
进阶思路:
这道题其实还可以把空间复杂度降为常数。
考虑到任何一根柱子,我们只需要知道它左右两边最高柱子中最小的那个的高度就可以算出来木桶的短板高度,从而可以算出接水量。我们又发现,如果采用的策略是从左边开始遍历,只需要一个数就能保存下当前的遍历到的柱子左边的最大高度,但是不知道右边的最大高度。反之如果从右边开始遍历,那么只能用一个数来保存遍历到的柱子右边的最大高度,而不知道左边的。
为什么只知道一边最大高度不行呢,这就要回到木桶原理,我们需要知道两边挡板中的最小值,只知道一边当然不能确定它就是最小的。
那么如果从左右两边同时开始遍历呢?问题突然豁然开朗,对于当前的所遍历到的左柱a和右柱b来说我们知道左柱a的左边最大高度柱子a_left_max,以及右柱b的b_right_max。那么a_left_max和b_right_max相比总要有个大小之分吧。如果a_left_max比b_right_max要矮,那么毋庸置疑柱子a_left_max作为挡板,连一个只可能比a_right_max矮的b_right_max都比不过,它一定比a_right_max要矮,从而对于a来讲也就没有找到a_right_max的必要了,直接用a_left_max的高度作为挡板高度即可。反之如果b_right_max比a_left_max要矮,b的接水量也就可以确定了。
这种思路的时间复杂度为O(n),空间复杂度为O(1)。
写码思路:
确定了解题思路,就可以开始构思如何写码了,抛弃掉所有的证明和理由,写码思路为:
题目要求得到接水总量,所以一个一个柱子去算,通过累加得到最终结果:从左边和右边同时开始进行,计算左右两边柱子的分别对应边的最大柱子高度,最大柱板高度小的那个算出接水量,然后向中心靠拢,直到两个指针相等进行最后一次计算,最终返回结果即可。
代码如下:
class Solution:
def trap(self, height: List[int]) -> int:
left, right = 0, len(height)-1
leftMax, rightMax = 0, 0
result = 0
while left<= right:
leftMax = max(leftMax, height[left])
rightMax = max(rightMax, height[right])
if leftMax <= rightMax:
result += leftMax - height[left]
left+=1
else:
result += rightMax - height[right]
right-=1
return result