题目描述
Given n non-negative integers a1, a2, …, an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container and n is at least 2.
大意是给一条从0开始的正整数数轴并且其长度为n,数轴上的第i个点对应着一个非零整数ai. ai的值的大小代表了这个点上立着一块高度为ai的隔板. 现在的问题是如何选取两个挡板构成一个容器, 使得该容器能装下最多的水.
注意: 容器不可倾斜并且n保证大于等于2.
解题思路
直观的解法无疑是肉身模拟, 用两层遍历的方式解决问题, 但这样一来, 复杂度就到了O(n2)O(n2), 并不尽如人意.
更快的解法自然是有的. 不妨这样想, 决定容器装水量无疑是它的绝对容积–即考虑到木桶效应的容积. 这时候计算装水量无非是简单的底*高, 底是隔板间的间隔, 高是其中较短的一块木板.
这里很明显的有两个变量, 底和高. 我们自然联想到采用控制变量的原则: 先固定其中一个量–或者是这个量的变化趋势, 再来讨论另一个量来简化计算.
注意到, 这一问题中, 假如我们从两头最边上的挡板开始取, 那么我们便可以控制底的变化趋势是递减的, 再来讨论高的变化–充满了变数的”高”.
由此自然得出了我们的O(n)O(n)算法:
- 从两头起, 计算底最大时的容积, 并维护和保持最大容积这个变量, 同时登记两块板中较短的那块板(容器高度)高度为最短挡板
- 从两边向中间移动, 显然这时底是递减的,, 要使得最大容积有变大的可能, 就要求最短挡板的高度变大. 因此从两边向中间移动时, 遇到比原来最短挡板还短的挡板直接跳过, 碰到更高的挡板时停止, 同时更新维护最大容积和最短挡板这两个值
- 重复Step 2, 直到两边都走到了中间会合. 算法停止并返回最大容积.
代码实现
给出Python的代码实现如下.
class Solution(object):
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
i = 0
j = len(height) - 1
water = 0
while i < j:
h = min(height[i], height[j])
water = max((j - i) * h, water)
while i < j and height[i] <= h:
i = i + 1
while i < j and height[j] <= h:
j = j - 1
return water