题目:
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
解题思路:
我们用i,j分别代表被选择的两条线的下标,首先理一下本题的成立的解具备的条件,有两条:
1. i,j都是合法的下标,即0<=i<=n,0<=j<=n
2. i在j的左边,即i<j
我们希望找到能容纳最多水的两个柱子,下来用图来表示需要搜索的解空间:
由于上述的两个约束,搜索空间为白色倒三角的部分。最开始想到的是暴搜的方法,i从前到后,每次循环中都计算与当前行的白色区域中的j对应的容纳水的面积,时间复杂度为o(n²),代码如下:
public int maxArea(int[] height) {
int maxArea = 0;
for (int i = 1; i < height.length; i++) {
for (int j = 0; j < i; j++) {
int area = height[i] > height[j] ? (i - j) * height[j] : (i - j) * height[i];
if (area > maxArea) maxArea = area;
}
}
return maxArea;
}
果然像往常一样,由于一个很长的测试用例,超时了:
因此考虑换一个方法,我们采用双指针缩减搜索空间的方法:
最开始我们检索最上方单元格(0,7),即考虑最左边的0号柱子和最右边的7号柱子,然后我们关注其中较短的一跟柱子。
左边的0号柱子较短,由于7号柱子已经是离0号柱子最远的了,水的宽度最对应最大,如果换其他的柱子和0号柱子配对,水的宽度只会更小,高度也不会增加,容纳水的面积只会更小,因此搜索空间排除如下:
此时7号柱子较短,此时7号柱子已经是离1号柱子最远的了,如果换其他的柱子和1号柱子匹配,水的宽度变小,高度不会增加,容纳水的面积只会更小,搜索空间排除如下:
可以看到,无论柱子i和j哪跟更长,我们都可以排除一行或者一列的搜索空间,经过n步以后,就能排除所有的搜索空间 ,只剩下最优解:
代码示例:
public int maxArea(int[] height) {
int fir = 0, end = height.length - 1;
int maxArea = 0;
while (fir < end) {
int tempArea = Math.min(height[fir], height[end]) * (end - fir);
if (tempArea > maxArea) maxArea = tempArea;
if (height[fir] < height[end]) {
fir++;
} else {
end--;
}
}
return maxArea;
}