P11 盛最多水的容器
题目链接:11. 盛最多水的容器.
题目描述
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
题解
方法一:双指针
思路
首先将使用双指针来解决这个问题的方法讲解一遍,再证明这种做法的正确性。
设置两个指针 left、right(它们所指向的元素值分别记为 x、y),开始时,left 指向数组的第一个元素,right 指向数组的最后一个元素,两指针指向的位置代表容器的左右边界,由以下公式计算出此时的可以容纳的水量:
- m i n ( x , y ) ⋅ ( r i g h t − l e f t ) min(x,y)\cdot(right-left) min(x,y)⋅(right−left)
然后将 x、y 中较小值对应的指针向另一个指针的方向移动(至于为什么要移动较小值对应的指针稍后会做出证明),然后按照上面的公式计算出此时可以容纳的水量。继续移动x、y 较小值对应的指针,每移动一次都要计算出此时可以容纳的水量,直到 left 与 right 相遇。最终的答案即为每一次以 left、right 为左右边界计算出的可以容纳水量中的最大值。
为什么要移动较小值对应的指针?
证明:记当前左右指针指向的元素值分别为 x 0 x_0 x0、 y 0 y_0 y0,不妨假设 x 0 < y 0 x_0<y_0 x0<y0,左右指针之间的距离为 d 0 d_0 d0,则此时可容纳的水量为: V 0 = m i n ( x 0 , y 0 ) ⋅ d 0 = x 0 ⋅ d 0 V_0=min(x_0,y_0)\cdot d_0=x_0\cdot d_0 V0=min(x0,y0)⋅d0=x0⋅d0
若将右指针向左移动,使其指向 y 1 y_1 y1,则此时可容纳的水量为 V 1 = m i n ( x 0 , y 1 ) ⋅ ( d 0 − 1 ) V_1=min(x_0,y_1)\cdot (d_0-1) V1=min(x0,y1)⋅(d0−1)
- 若 y 1 < x 0 y_1<x_0 y1<x0,则 m i n ( x 0 , y 1 ) = y 1 < x 0 min(x_0,y_1)=y_1<x_0 min(x0,y1)=y1<x0
- 若 y 1 ⩾ x 0 y_1\geqslant x_0 y1⩾x0,则 m i n ( x 0 , y 1 ) = x 0 min(x_0,y_1)=x_0 min(x0,y1)=x0
V 1 = m i n ( x 0 , y 1 ) ⋅ ( d 0 − 1 ) ⩽ x 0 ⋅ ( d 0 − 1 ) V_1=min(x_0,y_1)\cdot (d_0-1)\leqslant x_0\cdot(d_0-1) V1=min(x0,y1)⋅(d0−1)⩽x0⋅(d0−1)
故
V 1 < V 0 V_1<V_0 V1<V0所以移动右指针(或者说移动较大值对应的指针),一定会导致得到的可容纳的水量小于未移动之前的可容纳水量。
若将左指针向右移动,使其指向 x 1 x_1 x1,则此时可容纳的水量为 V 2 = m i n ( x 1 , y 0 ) ⋅ ( d 0 − 1 ) V_2=min(x_1,y_0)\cdot (d_0-1) V2=min(x1,y0)⋅(d0−1)
- 若 x 1 ⩽ x 0 x_1\leqslant x_0 x1⩽x0,则 m i n ( x 1 , y 0 ) = x 1 ⩽ x 0 min(x_1,y_0)=x_1\leqslant x_0 min(x1,y0)=x1⩽x0
- 若 x 1 > x 0 x_1> x_0 x1>x0,则 m i n ( x 1 , y 0 ) > x 0 min(x_1,y_0)>x_0 min(x1,y0)>x0
可能出现
V 2 > V 0 V_2>V_0 V2>V0所以移动左指针(或者说移动较小值对应的指针),可能会得到的可容纳的水量大于未移动之前的可容纳水量。
所以我们应该移动较小值对应的指针。
算法
class Solution {
public:
int maxArea(vector<int>& height) {
int size=height.size();
int left=0;
int right=size-1;
int ans=0;
while(left<right){
int temp=(right-left)*min(height[left],height[right]);
ans=temp>ans?temp:ans;
if(height[left]>height[right]){
--right;
}
else{
++left;
}
}
return ans;
}
};
复杂度分析
假设数组中元素的个数为 n n n
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)