题目
11. 盛最多水的容器
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 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
示例 3:
输入:height = [4,3,2,1,4]
输出:16
示例 4:
输入:height = [1,2,1]
输出:2
解法
思路
暴力的循环遍历,稍微优化逻辑,省去一些不需要的计算。 具体见代码。
代码如下
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let len = height.length;
let S = 0;
// 遍历数组,用每一项去和剩下的高度做乘法,取最大的面积,中间有判断优化
for(let i=0;i<len;i++){
let maxHeight = height[len-1];
// 左边柱从左往右遍历,右边柱则从右往左遍历
for(let j=len-1;j>=i;j--){
let width = j-i;
// 如果右边柱的高度大于左边柱,那么右边柱就不用再往前找了,往前再找面积也不会大,面积当前项*宽度
if(height[j]>height[i]){
S = Math.max(S,height[i]*width);
break;
}else{
// 如果右边柱小于左边柱,且小于最大面积高度,初始最大面积高度为右边柱高度
// 那么后面的逻辑不用走了,跳过本次循环
if(height[j]<maxHeight){
continue
}
// 如果右边柱小于左边柱,但高度大于最大面积高度,那么计算以下这根柱子与当前左边距的面积,看看比目前的最大面积是不是会大一些
let newS = Math.max(S,height[j]*width);
if(newS>S){
maxHeight = height[j]
S = newS
}
}
}
}
return S;
};
解法二
思路
在上一解法的基础上,进一步优化,使用双指针法,可以进一步省去无效计算。
该方法的核心思想
左右指针对应的高度,相比较,如leftHeight<rightHeight
那么我们就将left向右移动一格,因为对于left来说,目前的面积瓶颈是left自己,left再去根右边right左边的柱子比较,都没有任何意义,面积不可能大于leftHeight*(right-left),所以left直接右移。
同理接下来如果rightHeight<如leftHeight,那么就right左移
代码如下
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let left = 0, right = height.length - 1;
let S = 0;
while (left < right) {
let newS = Math.min(height[left], height[right]) * (right - left);
S = Math.max(S, newS);
if (height[left] <= height[right]) {
++left;
}
else {
--right;
}
}
return S;
};