📗盛最多水的容器 算法刷题打卡 第二天

难度：Medium

题目：

给定一个长度为 n 的整数数组 height 。有 n 条垂线，第 i 条线的两个端点是 (i, 0) 和 (i, height [i]) 。

找出其中的两条线，使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明：你不能倾斜容器。

解析

一眼 “秒” 了？

粗看这道题，这不是很简单吗。用两个循环，循环计算水量并比较最大水量不就行了。甚至不需要记录在哪个时刻水最大。“秒” 了

Copy
public int maxArea(int[] height) {
//遍历
int max = height.length;
int maxarea = 0;
for (int n = 0; n < max; n++) {
for (int m = n; m < max; m++) {
int temp = (m - n) * Math.min(height[n],height[m]);
maxarea = Math.max(maxarea,temp);
}
}
return maxarea;

}

结果

好家伙，这么搞。数据太多了，两个循环导致处理超时了。

重新出发

问题是两个循环嵌套导致的，所以目标就是减少循环，做到一次循环甚至更少。
看了提示需要用两个指针。从左右端点作为出发点。如果能做到一次只移动一个方向上的指针，那个就可以做到只循环一次。水量由水池的底（就是两个指针的距离）和高（两个指针所对应的高的最小值）的乘积决定。水池的底，在向中移动过程中只会不断变小。而高是两个指针对应高的最小值，如果说移动高的指针因为取的是最小值所以水池的高没有变化。水量甚至还会变小。移动水池低的边才有可以增加水量。
于是乎，就决定方式为循环一次，每次移动小的边，比较每次的水量和记录的最大水量，找出真正的最大水量。

Copy
public int maxArea(int[] height) {
//双指针
int max = height.length;
int i = 0;
int j = max - 1;
int maxarea = 0;
for (int n = 0; n < max; n++) {
maxarea = Math.max(maxarea, (j-i)*Math.min(height[i],height[j]));
if (height[i] < height[j]) {
i++;
} else {
j--;
}
}
return maxarea;
}

结果成了

继续优化

我发现，在左右指针向中间移动的过程中。如果移动后的指针高度比移动前更低，那是不是就不用判断水池 “水量”，因为肯定变少了，直接继续移动。于是我用了两个标识来记录，提高代码的执行速度。
于是嘎嘎干

Copy
public int maxArea(int[] height) {
//双指针优化
int max = height.length;
int i = 0;
int j = max - 1;
int maxarea = 0;
//两边的标识
int maxHeightI = height[i];
int maxHeightJ = height[j];
//记录上次是左边指针（0）移动还是右边指针（1）移动
int flag = 0;
for (int n = 0; n < max; n++) {
if (flag == 0) {
if (height[i] < maxHeightI) {
i++;
continue;
}else {
maxHeightI = height[i];
}
}
if (flag == 1) {
if (height[j] < maxHeightJ) {
j--;
continue;
}else {
maxHeightJ = height[j];
}
}
maxarea = Math.max(maxarea, (j-i)*Math.min(height[i],height[j]));
if (height[i] < height[j]) {
i++;
flag = 0;
} else {
j--;
flag = 1;
}
}
return maxarea;

}

结果果真有效