题目描述
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 the line i
is at (i, ai)
and (i, 0)
. Find two lines, which, together with the x-axis forms a container, such that the container contains the most water.
Notice that you may not slant the container.
Example 1:
Input: height = [1,8,6,2,5,4,8,3,7]
Output: 49
Explanation: The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.
这个题一开始的思路是暴力遍历,复杂度.
看了一下题解,发现了一种线性解法,但是题解的解释有点太云里雾里,不太明白。最后自己考虑了一下之后发现其实是这样的:
首先是所有的线段是密集排布的,也就是说每个线段之间的间隔都是1.这样,如果从最外侧开始计算,记左边线段为L, 右边线段为R,他们之间的距离为D.
那么面积就是min(L,R)*D.
于是我们可以推断出一个事实:
那么就是因为我们是从最外侧开始计算,那么移动高的的那一侧是没有意义的,因为无论移动后高度变为多少,他的水面高度还是取决于矮的那侧。而且因为距离变短了。于是如果移动高的我们只可能得到一个比当前更小的面积。
因为新的D一定是小于原来的D,而且新的最矮线段绝对不大于原来的最矮值。
用公式表示就是,因为min(L', R') <= min(L,R) and D' < D. 所以min(L', R')*D' 一定小于min(L,R)*D。
所以我们只能移动最短的那根线段。每移动一次就计算一次面积,然后再移动像对短的线段。而这样知道左侧等于右侧。我们就可以得到最终答案了。
比较抽象的地方是怎么通过n的操作排除掉了n^2的可能性。我觉的可以辅助图片来理解,以题目例题为例,一开始的情况为:
蓝色表示L的index,黄色表示R的index,因为L必须小于R,所以红色的为不可能的情况。
之后白色就是我们需要判断的所有可能了,很明显是个n^2的问题。
此时L=0, R=8
但是基于我们上面的推论,我们只能右侧,因为左侧高度为1,无论怎么移动右侧,都不可能得到一个大于当前面积的新面积了。于是我们直接排除了L=0时的所有可能。
同样的步骤,因为现在R为矮的一侧,我们可以排除所有 R = 8的所有可能
以此类推,我们每次都是排除n可能,所以,我们可以用n次操作来排除n^2的可能。
而过程中的最大值,就是我们的答案
代码:
class Solution:
def maxArea(self, height: List[int]) -> int:
maxArea = 0
i = 0
j = len(height)-1
while i < j:
area = j-i
if height[i] < height[j]:
area *= height[i]
i += 1
else:
area *= height[j]
j -= 1
maxArea = area if area > maxArea else maxArea
return maxArea