携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情
题目描述
给定一个长度为 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
提示:
n == height.length2 <= n <= 1050 <= height[i] <= 104
解题思路——暴力求解
这不是普通的暴力求解,这是王维诗里的暴力求解,手动狗头/.
一开始是没有想用暴力的,顶级前端怎么能用暴力草草了事呢?于是我左思右想辗转反侧,然而脑力里的暴力求解法挥之不去,仿佛不用暴力解一遍我就不会似的(可不就是呢)。
没有办法,只好妥协。
这道题就是求面积,两根板之间可乘水量的最大面积。根据短板理论,一只木桶盛水的多少,并不取决于桶壁上最高的那块木块,而恰恰取决于桶壁上最短的那块。好耶,这不信手拈来了?
只要用 双层循环 两两将木板的面积求出来,最后得出最大的面积,不就完事了。
噼里啪啦,提交,超时,很快啊,心凉了半截,在这个炎热的夏天里格外的特别。
跑路是不可能跑路的,也不会别的解法了,超时了就得想办法优化,既然盛水多久取决于短板,那如果我的左边板 height[left] * height.length 比当前最大面积还小的话,内层循环就直接跳过了。
题解
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
const lens = height.length;
let max = 0;
for(let i=0; i<lens; ++i) {
if(height[i] * (lens - 1) < max) continue;
for(let j=i+1; j<lens; ++j) {
max = Math.max(Math.min(height[i], height[j]) * (j - i), max);
}
}
return max;
};
解题思路——双指针
先看图:
还是基于短板理论:木桶可盛的最大水量是基于最短的那条板。
拿这个阶段来讲解一下,i 和 r 这两块中,短板为 i,如果能够移动这两个板中的其中一块,使得新的面积 有可能 大于当前 i 和 j 组成的最大面积 S(i, j),那么移动谁的可能性更大?
假设法走一走。假设我们移动的是最高的板 j,那么下一个板 j - 1 的高度 可能大于板i,那么此时最短板高度为依然是 height[i],根据短板理论,意味着面积依然是由 height[i] 决定的;如果下一个板 j - 1 的高度小于板 i,那么最短板的高度为 height[j - 1],组成的面积比不移 height[j] 时 更小了。也就是说,如果移动最高的板,那么结果只能是 移动后的面积 S2 <= 移动前的面积 S1。
如果移动的是最短的板呢,那么 height[i + 1] 的高度可能高于 height[i],如果高于 height[i] 的话,那么 移动后的面积 S2 > 移动前的面积 S1;如果小于 height[i] 的话,那么 移动后的面积 S2 < 移动前的面积 S1。
那么题目要求我们找到最大盛水的板子,如果搁这移最长的板子,你寻思寻思你能找到啥?
题解
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
const lens = height.length;
let l = 0, r = lens - 1, max = 0;
while(l < r) {
max = height[l] > height[r] ?
Math.max((r - l) * height[r--], max)
: Math.max((r - l) * height[l++], max);
}
return max;
};