这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战
题目
给你 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
思路
题目寻找的是两个位置 x、y,它们构成的容器,可以容纳最多的水
很容易想到,暴力解法。遍历题目所给数组 height,每次取出一个,作为容器的 左边界x,再从该位置往后的数据中,每次取出一个,作为容器的 右边界y,此时容器能够储存的水量 Size = Math.min(height[x], height[y]) * (y - x),依次遍历剩下的数据作为 右边界y,每次都和上一次的储水量比较,留下较大的 Size,最后留下的 Size 即为题目所求
最终代码
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let size = 0;
for(let i = 0; i < height.length; i++) {
for(let j = i + 1; j < height.length; j++) {
let x = j - i;
let y = Math.min(height[i],height[j]);
let maxHeight = x * y;
if(maxHeight > size) {
size = maxHeight;
}
}
}
return size;
};
思考
上述的暴力解法,时间复杂度为 O(n!),当n很大的时候,算法的时间复杂度太高了,性能必然很低
这里介绍双指针的解法:
-
新建两个指针 x, y,初始时分别指向数组的左右两端,储水量的计算公式依然为
Math.min(height[x], height[y]) * (y - x)
-
x
指针初始在数组前端,只能向数组末端移动,y
指针初始在数组末端,只能向数组前端移动 -
关键: 每次移动
height[i]
数值较小的那个指针,重新计算储水量 (👉为什么看官方解释 😭😭😭
) -
每次比较保留储水量较大的数值
-
双指针相遇时,遍历结束
代码如下
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let x = 0, y = height.length - 1;
let size = 0;
while(x < y) {
let area = Math.min(height[x], height[y]) * (y - x);
size = Math.max(size, area);
if(height[x] <= height[y]) {
x++
} else {
y--
}
}
return size;
}
小结
遇到计算两个位置之间构成容积的问题,可以考虑双指针的思想,但是解法中,每次移动较小的那一个指针是不容易想到的,反而可能会去考虑各种移动情况,导致思路错乱,最后写不出解法。
解题思路 👉👉👉👉👉👉👉👉👉👉👉👉👉 解过了才有思路
解题模版 👉👉👉👉👉👉👉👉👉👉👉👉👉 解多了才有模版
LeetCode 👉 HOT 100 👉 盛最多水的容器 - 中等题 ✅
LeetCode 👉 HOT 100 👉 最长回文子串 - 中等题
✅
LeetCode 👉 HOT 100 👉 无重复字符的最长子串 - 中等题
✅