盛最多水的容器(C#)
题目描述:给定 n 个非负整数 a,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2
1.暴力法
思路:遍历数组,找到面积最大的区域并保存。
暴力法没什么好说的,直接上代码:
public static int MaxArea1(int[] height)
{
int result = 0;
for (int i = 0; i < height.Length; i++)
{
for (int j = i+1; j < height.Length; j++)
{
int h = height[i];
h = height[j] > h ? h : height[j];
int n = h * (j - i);
result = n > result ? n : result;
}
}
return result;
}
简单粗暴,显然时间复杂度为O(n2),空间复杂度为O(1)。
2.双指针法
思路:最基本的想法,面积的大小取决于两端中矮的一端的高度以及两端之间的距离。所以,我们从数组左右两端出发,将较矮一端处的指针向较高一端处移动,直到两个指针相遇。
这种思路简单的理解就是要想面积变大,就必须要将较矮端的指针移动,以为移动较高一端的指针只能使得面积变小。(当然这种想法比较粗糙,而且是在已经知道双指针法有效的基础上进行的推测)
ok,接下来进行严密的证明:
要证明的命题是:双指针法的移动路径一定会经过构成最大面积的两个位置。
首先我们假设有两个位置m和n构成最大面积(m<n,m和n的高度不确定,后面会分情况仔细讨论)。
由双指针法的移动方式可知,这个命题等价于m左侧点的高度都小于等于m的高度,n右侧点的高度也都小于等于n的高度。
我们假设在m左侧有一点的高度大于m的高度,那么就有以下三种可能性:
①m的高度小于n的高度,则有Areamn=(n-m) * m以及Areanp=(n-p) * max(p,n)
显然,n-p>n-m且max(p,n)>m,所以Areanp>Areamn。
②m的高度等于n的高度,则有Areamn=(n-m) * m以及Areanp=(n-p) * n
显然,n=m且max(p,n)>m,所以Areanp>Areamn。
③m的高度大于n的高度,则有Areamn=(n-m) * n以及Areanp=(n-p) * n
显然,n-p>n-m,所以Areanp>Areamn。
综上,不论哪种情况Areanp都大于Areamn,即np构成的面积会大于mn构成的面积,这与假设“m和n构成最大面积”不符。所以m的左侧不存在一点的高度大于m的高度,即m左侧点的高度都小于等于m的高度,得证。反之亦然。
ok,有了思路以及证明之后就是代码了。
public static int MaxArea2(int[] height)
{
int result = 0;
int i = 0;
int j = height.Length - 1;
while (i < j)
{
int area = (j - i) * Math.Min(height[i], height[j]);
result = area > result ? area : result;
if (height[i] < height[j])
i++;
else
j--;
}
return result;
}
这个方法就很棒了,时间复杂度是O(n),空间复杂度是O(1)。