题目描述
给定 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。
题目分析
题目从理解上还是比较简单的,即是在数组a中取一组i,j,使得min(a[i],a[j])×(j-i)的值最大。从这个式子来看,影响最终结果的主要有两个因素:a[i]和a[j]中的较小值和i和j的距离。要求解这道题,最简单的就是暴力求解了,即是对数组中每两个元素均进行计算,找出其中的最大值即可,不过这种方法的算法时间复杂度达到了O(N²),显然不是我们想要的,我们应当向O(N)的时间复杂度考虑。那么该怎么考虑呢?
还是从min(a[i],a[j])×(j-i)这个式子来入手,前面说了,影响这个式子就两个因素,为了使结果尽量大并且便于求解,那么一开始要么应该使得min(a[i],a[j])尽量大,要么使得j-i尽量大,显然,在没有遍历数组的情况下前者是无法做到的,但是使j-i最大是能够做到的,即让i和j分别指向头尾。
在初始情况下,i指向数组头,j指向数组尾,由i和j即可得出当前的容器容积,那么下一步该怎么移动i和j呢?是只移动i呢还是只移动j呢还是i和j同时移动呢?显然不应该两者同时移动,因为这样很可能会漏掉实际的最大值。
这里如果仔细思考的话,可以发现:假设a[i]和a[j]中a[i]是较小值,那么a[i]×(j-i)就已经是所有以i为边界的容器容积的最大值了,为什么这么说呢?因为a[i]是较小值,任取一个k,其结果必定是小于(a[k]<a[i])或者等于(a[k]>=a[i])a[i]×(k-i)的,而由于k<=j,因此此时以i为边界的容器最大值必定是a[i]*(j-i)。如果下一步移动j的话,那么i是不变的,这样根据前面的分析,所得到的新的容器的容积肯定是不超过当前值的,因此下一步必定移动i,即是让i++,j不变,再计算新的容器的容积,如果新的容积值大于前面的容积值,那么就用变量max存储这个值。移动后,决定新容器容积大小的即是新的a[i]与a[j]之间最小值了,然后下一步即是将a[i]和a[j]中的较小值所对应的i或者j往另一边移动即可,以此类推…
因此,在每一步操作中,计算完当前容器容积大小后,只需比较a[i]与a[j],如果a[i]较小,那么下一步就应该是i++,否则就应该是j–,最终求得最大值。程序如下:
class Solution {
public:
int maxArea(vector<int>& height) {
int max=0;
int s=height.size();
int i=0,j=s-1;
while(i<j)
{
int temp=min(height[i],height[j])*(j-i);
if(temp>max)max=temp;
if(height[i]>=height[j])j--;
else if(height[i]<height[j])i++;
}
return max;
}
};