问题
给定一个长度为 n
的整数数组 height
。
有 n
条垂线,第 i
条线的两个端点是 (i,0)
和 (i,height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。
说明:你不能倾斜容器。
问题分析
设两指针i,j ,指向的水槽板的高度分别为,此状态下水槽面积为.
由于可容纳水的高度由两板中的 短板 决定,因此可得如下 面积公式 :
无论长板或短板向中间收窄一格,都会导致水槽 底边宽度 −1,变短:
- 若向内移动短板,水槽的短板可能变大,因此下个水槽的面积可能增大。
- 若向内移动长板,水槽的短板不变或变小,因此下个水槽的面积一定变小。
因此,初始化双指针分列水槽左右两端,循环每轮将短板向内移动一格,并更新面积最大值,直到两指针相遇时跳出,即可获得最大面积。
算法流程
- 初始化双指针i,j分别指向水槽两边。
- 更新最大面积,选择两板中较短的板子向中间收窄一格,重复至两针相遇时跳出。
- 返回最大面积。
代码
int maxArea(vector<int>& height) {
int i = 0; // 左边界
int j = height.size() - 1; // 右边界
int maxArea = 0; // 最大面积
while (left < right) {
int minHeight = min(height[left], height[right]); // 找到当前两根柱子中的较短柱子
int res= (right - left) * minHeight; // 计算当前区域的面积
maxArea = max(maxArea, res); // 更新最大面积
// 移动较短柱子的指针,以寻找可能更大的面积
if (height[i] < height[j]) {
i++;
} else {
j--;
}
}
return maxArea;
}
以上代码可优化如下:
int maxArea(vector<int>& height) {
int i = 0,j = height.size()-1,res = 0;
while(i<j)
{
res = height[i] < height[j] ?
max(res,(j-i) * height[i++]):
max(res,(j-i) * height[j--]);
}
return res;
复杂度分析
- 时间复杂度O(N):双指针遍历一次底边宽度N。
- 空间复杂度O(1) :变量 i,j,res使用常数额外空间。
Q&A
双指针相等 的情况下,移动不同的值是否有可能错过最大面积?
答:不会。
i和j的高度是相同的,而面积最大值取决于短边;当i和j的高度相同时,移动任何一边,如果变矮了,那面积会变小;如果变高了,面积也不会变大。而下一次移动的时候面积才会发生变化。也就是说i和j都必须往里缩小一次才有可能出现更大的面积。