题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/container-with-most-water
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
给你 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
可能很多人刚看到这道题第一想法就是暴力法—就是每两个垂直线都算一遍容水量,然后选最大值。这个我也想到了:
class Solution {
List<List<Integer>> output = new LinkedList();
int n;
int k;
//回溯
public void backtrack(int first, LinkedList<Integer> curr) {
// 如果组合中的个数达到要组合的数量,则加入到列表中
if (curr.size() == k)
output.add(new LinkedList(curr));
//遍历把每个数都加入列表,最后又移除
for (int i = first; i < n + 1; ++i) {
// add i into the current combination
curr.add(i);
// use next integers to complete the combination
backtrack(i + 1, curr);
// backtrack
curr.removeLast();
}
}
//从n个数里获取k个数的组合
public List<List<Integer>> combine(int n, int k) {
this.n = n;
this.k = k;
backtrack(1, new LinkedList<Integer>());
return output;
}
//依次计算每个组合中的容纳水的量,并取最大值
public int maxArea(int[] height) {
List<List<Integer>> rs = combine(height.length,2);
int max = 0;
for (int i=0;i<rs.size();i++){
List<Integer> temp = rs.get(i);
int a = temp.get(0);
int b = temp.get(1);
max = Math.max(max,(b-a)*Math.min(height[a-1],height[b-1]));
}
return max;
}
}
很遗憾的是,,这个方法超时了,或许可以继续优化这种方法,但是我没有再去优化。因为想起了之前的”双指针法”
我们可以一个指针指向最左边,一个指向最右边,计算一次容水量,并和原来的容水量作比较(最开始可以初始化为0),然后选择较大的一个。
接下来关键的一步就是移动指针,那么到底是==**移动左指针还是右指针呢?**我们可以想一想,如果左指针当前指的垂直线高度较高,那么它的容纳性就更高,换句话说,我们就是要移动垂直线高度较小的一边,以便能尽量获取更大的容水量。
下面是实现代码:
//双指针法
public int maxArea(int[] height) {
//初始化最大容水量为0
int max = 0;
//l为左指针,r为右指针,初始先放在左右两端
int l=0,r = height.length-1;
while (l<r){
//取较大的容水量,并更新
max = Math.max(max,(r-l)*Math.min(height[l],height[r]));
//如果左边的垂直线高度比右边的低,就把左指针往右移动,
//否则就把右指针向左移动
if (height[l]<=height[r]){
l+=1;
}else {
r-=1;
}
}
return max;
}