题目
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明: 你不能倾斜容器。
思路
单调栈(暂无解)
最开始想的是用单调栈来解答这道题,从栈底到栈顶依次递减,遇到大于栈顶的就弹出,更新结果res,最后再清算栈内的元素,但是这样做出来,覆盖不了全部数据。后面又想到了2个栈进行弹出,一个栈顶到栈底依次递减,一个栈顶到栈底依次递增,但是后面写出来了,发现中间如果在中间某处破坏了单调性,导致更新的res不是最大的值。后面有时间再想想这个,看看有没有机会写出来。
暴力解法-两重循环
通过两个for循环,找到所有的面积,然后取最大面积。 时间复杂度:O(N^2),空间复杂度:O(1)
代码一-暴力双循环
class Solution:
def maxArea(self, height: List[int]) -> int:
i = 0
res = 0
n = len(height)
for i in range(n):
for j in range(i+1, n):
res = max(res, min(height[i], height[j])*(j-i))
return res
双指针-移动短边
需要移动两头问题,可以考虑双指针。
难点:如何移动指针?
- 相同情况下,两边距离越远越好
- 区域受限于较短边
距离越远越好,所以右指针从最右开始,保证宽度达到最长; 在向左(向右)扫描时,如果右边(左边)比较短或者和原来的一样长,可以跳过面积的计算,因为原来宽度和高度更大,所以现在面积只会更小。
如何决定移动左指针还是右指针?
考虑第二特性,区域受限于较短边,如果右边比左边长,那么我们应该移动左指针,保留右指针,因为短的边限制了面积增大可能性,移动左指针只会导致宽度变小,寻找更好的左指针点。反之同理。
左右指针分别从头尾出发,每次只固定较长的边的指针,短边指针向内移动,计算该边变长后的面积,同时决定是否要交换移动顺序。
代码二:双指针-简洁版
class Solution:
def maxArea(self, height: List[int]) -> int:
i = 0
j = len(height) - 1
res = 0
while i < j:
res = max(res, min(height[i], height[j]) * (j - i))
if height[i] >= height[j]:
j -= 1
elif height[i] < height[j]:
i += 1
return res
代码三:双指针-优化版
将下面这段话加入代码: 在向左(向右)扫描时,如果右边(左边)比较短或者和原来的一样长,可以跳过面积的计算,因为原来宽度和高度更大,所以现在面积只会更小。
每次移动指针的时候,通过判断找到左边or右边下一个更大的高度,避免res的重复计算,减少计算量。
class Solution:
def maxArea(self, height: List[int]) -> int:
i = 0
j = len(height) - 1
res = 0
while i < j:
res = max(res, min(height[i], height[j]) * (j - i))
if height[i] >= height[j]:
while i < j and height[j - 1] <= height[j]:
j -= 1
j -= 1
elif height[i] < height[j]:
while i < j and height[i+1] <= height[i]:
i += 1
i += 1
return res