LeetCode 热题100——day05-盛水最多的容器
题目
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
题目要求
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
解题思路
先读题,这题是要求这个面积问题,面积公式就是:(右侧下标 - 左侧下标) * Math.min(右侧下标, 左侧下标);
思路
既然要求最大值,那么可以使用for的循环嵌套去做,定义一个变量装最大值max,外层for循环遍历数组,内层从外层的下一个元素继续去遍历,如果算出来的面积值大于max中装的最大值,那就将max重新赋值。这个思路看似可以去解决问题,但是这是暴力枚举法,时间复杂度相当高,都达到O(n^2)了,如果这个数据量非常庞大,指数级别的增长,真是不敢想象的时间复杂度啊。所以我们不要这样去做。
**换一个思路:**如果我们同时从左右两边开始,首先面积这个最大值就是进行比较取最大,这部分比较简单。
核心就是什么时候动指针,动那个?:就是,每次进行计算,当前最大和当前计算面积取最大放max,然后移动左右相对来说高度低的那侧的指针,因为能够确保去拿一个相对来说更高的高度,这样便能取最大到max中,因为取小的永远不会大过那个大的的面积。这样时间复杂度是不是直接将为O(n)了!
明白这点,来看解题1吧。
剪枝:要降低复杂度我们可以想剪枝操作,剪枝的条件很简单,就是取一个最小值,去装每次比较出来最低的那个数,因为如果新出来的数小于等于那个被去掉的小的数,那么还不如不算,因为x轴上的长度一直在减小,小于等于之前被比下去的数再乘当前这个数,肯定是要比最大值小的啊,即使是等于也会小(1*当前y轴最大的值)平方,所以这类数压根不用计算,直接剪枝就行。来看解题2吧。
解题答案
解题1:
public static void main(String[] args) {
System.out.println(maxArea(new int[]{1,8,6,2,5,4,8,3,7}));
}
public static int maxArea(int[] height) {
int max = 0;
int left = 0;
int right = height.length - 1;
while (left < right) {
int area = Math.min(height[left], height[right]) * (right - left);
max = Math.max(max, area);
if (height[left] < height[right]) { // 移动相对小的那边的指针
left++;
} else {
right--;
}
}
return max;
}
解题2
public static void main(String[] args) {
System.out.println(maxArea(new int[]{1,8,6,2,5,4,8,3,7}));
}
public static int maxArea(int[] height) {
int max = 0;
int minHeight = 0;
int left = 0;
int right = height.length - 1;
while (left < right) {
while (height[left] <= minHeight && left < right) { // 剪枝 + 移动指针
left ++;
}
while (height[right] <= minHeight && left < right) { // 剪枝 + 移动指针
right --;
}
minHeight = Math.min(height[left], height[right]); // 取两边小的那个值,也就是低的那个边长为下一次剪枝的比较值
max = Math.max(max, minHeight * (right - left));
}
return max;
}