题目
给定 个非负整数 ,每个整数对应一个坐标为 的点。这样形成了 条竖直的线段,线段的两个端点分别为 和 。
要求找到两条线段当做两个挡板,使得他们与 x 轴组成的容器可以盛最多的水,并输出最大容量。
示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
解释: 在第2根线(8)与最后一根线(7)之间的容量最大,容量为
。
思路
1、暴力算法
最容易想到的算法为暴力算法,即编写一个双重循环,遍历所有不同两根线段,计算线段间的容量,选出最大容量。
这时候算法复杂度为 。
2、逼近法
定义两个指针,分别指向最左边的线段和最右边的线段,然后逐渐将两个指针往中间移动,每次移动后计算一边当前的容量,然后选择其中最大的容量。但是这样做不会遍历所有不同的两根线段,如何保证一定会遍历到最大容量的两根线段呢?
策略是,每次移动时,将左右指针的两根线段中的较短的线段,往中间移动一位。
为什么要这样做?因为容器的容量,是由两根线段中较短的那根决定的,改变短线段可以改变容量。
严格的证明:
图示是很多线段中的一部分,不失一般性地,比如某一次迭代之后,左右指针已经移动到了
和
的位置(即蓝色轮廓),下一步该选择是
向右移动一位,还是
向左移动一位。
假如我们最后的答案是 和 之间的容量是最大的(即红色线段),其中 和 是同一个线段。如果 能一直向右移动,直至和 重合,那么说明算法成功,找到了最大容量的 线段。
那么有没有可能出现 向左移动一位,导致 位于 和 之间呢?如果这样,那么之后无论再怎么移动,都不可能找到最优解 了,算法也就失败了。
结论是: 不可能位于 和 之间。
可以用反证法证明:
假如下一步是 向左移动一位,那么根据移动策略,现在必然有 的高度大于 的高度,才会使得 左移。这样,一定会有 和 组成的容量大于 和 组成的容量,因为:
而 在 左边,距离 的距离更远,因此有:
其中, 表示容量, 表示高度。
这和 是最大容量相矛盾。因此,逼近法是有效的,肯定会找到正确答案。
逼近法的时间复杂度为 。
python实现
def maxArea(height):
"""
:type height: List[int]
:rtype: int
从两边往中间寻找。
"""
max_area = 0
l = 0
r = len(height) - 1
while(l < r):
max_area = max(max_area, min(height[l], height[r]) * (r - l))
if height[l] < height[r]:
l += 1
else:
r -= 1
return max_area
if '__main__' == __name__:
height = [1,8,6,2,5,4,8,3,7]
print(maxArea(height))