LeetCode 热题 HOT 100 Java题解
11. 盛最多水的容器
题目:
给你
n
n
n 个非负整数
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an,每个数代表坐标中的一个点
(
i
,
a
i
)
(i, a_i)
(i, ai) 。在坐标内画
n
n
n 条垂直线,垂直线
i
i
i 的两个端点分别为
(
i
,
a
i
)
(i, a_i)
(i, ai) 和
(
i
,
0
)
(i, 0)
(i,0)。找出其中的两条线,使得它们与
x
x
x 轴共同构成的容器可以容纳最多的水。
说明: 你不能倾斜容器,且 n n n 的值至少为 2 2 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
双指针
本题非常关键的一点就是两个指针 i i i, j j j所构成的面积 S = M a t h . m i n ( h e i g h t [ i ] , h e i g h t [ j ] ) ∗ ( j − i ) S = Math.min(height[i], height[j]) * (j - i) S=Math.min(height[i],height[j])∗(j−i),面积是由 x x x轴长度和两个指针指向的垂直线中短的那根所决定的。
因此,我们将两个指针放在两端开始向内收缩,由于 j − i j - i j−i是一定变小的,所以我们考虑收缩哪个指针,如果收缩的是指向垂直线长的那个指针,则面积一定是会不变或变小的,而若我们收缩短的那个指针,则面积是有可能变大的,抓住这点,我们每次都可以通过比较两个指针指向垂直线的长短,来决定唯一移动一个指针,这样就把复杂度从 O ( n 2 ) O(n^2) O(n2)降到了 O ( n ) O(n) O(n)了。
class Solution {
public int maxArea(int[] height) {
int i = 0, j = height.length - 1;
int max = 0;
while(i < j) {
max = Math.max(Math.min(height[i], height[j]) * (j - i), max);
if (height[i] < height[j]) i++;
else j--;
}
return max;
}
}
这样的答案时间为4ms,有一个小优化在于:
如果短的那个指针收缩之后指向的垂直线没变或更短了,则没有必要计算面积,可以直接继续收缩,可以通过循环来减少计算面积的次数。
class Solution {
public int maxArea(int[] height) {
int i = 0, j = height.length - 1;
int max = 0;
while(i < j) {
max = Math.max(Math.min(height[i], height[j]) * (j - i), max);
if (height[i] < height[j]) {
int left = height[i];
while(i < j && height[i] <= left) i++;
}
else {
int right = height[j];
while(i < j && height[j] <= right) j--;
}
}
return max;
}
}
这样答案时间就减少到了2ms,击败了99%。
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n)
两个指针收缩遍历一遍数组。
-
空间复杂度: O ( 1 ) O(1) O(1)
只使用常数个变量的额外空间。