嗨!~ 大家好,我是YK菌 🐷 ,一个微系前端 ✨,喜欢分享自己学到的小知识 🏹,欢迎关注我呀 😘 ~ [微信号:
yk2012yk2012,微信公众号:ykyk2012]
「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」
简单题重拳出击,中等题我也不带怕的!!今天来一道中等题,其实这道题编码很简单,但是这个思想很重要。
11. 盛最多水的容器(中等)
给你
n个非负整数a1,a2,...,an,每个数代表坐标中的一个点(i, ai)
在坐标内画n条垂直线,垂直线i的两个端点分别为(i, ai)和(i, 0)
找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。
【解法】暴力枚举
一拿到这题,不想思考就直接双重循环暴力枚举出所有结果,然后返回最大值即可。
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let maxArea = 0
for(let i = 0; i < height.length - 1; i++){
for(let j = i + 1; j < height.length; j++){
let h = Math.min(height[i], height[j])
let thisArea = (j - i) * h
maxArea = Math.max(thisArea, maxArea)
}
}
return maxArea
};
结果当然就是超时了!!!
【解法】双指针
暴力解法时间复杂度为,显然效率太低了。我们再仔细想想,真的有必要暴力枚举出所有的情况吗?
首先我们思考,容器中水的容量是由哪些因素组成的,就是 底 * 高 底就是两个垂直线下标之间的差,而高就是两条垂直线中最小的那一个。 我们拿两个指针i,j分别指向两个垂直线,所以想得到盛最多水的容器,应该是i和j距离尽可能远的情况下,找一个i和j中小的那个也不是太小的...有点拗口
所以我们可以转换思路,我们将i和j一开始就直接设置在数组的两端,计算当前可以容纳的水的容量。
接下来就要移动指针了,移动哪一个呢? 向内移动值小的那个指针。
为什么要这样做呢? 我们这样想,如果你移动值较大的那个指针,较小的指针肯定决定了未来高度的上限,而ij之间的距离会变小,也就是说,容量只会减不会增,移动大值的那个指针的所有操作都是不需要的。
所以我们总结一下
初始时,两个指针分别指向数组的两端,得到当前可以容纳的水的容量;每次移动一个指针,就是指向的值小的那个指针。
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let i = 0;
let j = height.length - 1;
let maxArea = 0;
while(i<j){
// 计算当前容量
let thisArea = Math.min(height[i],height[j]) * (j - i);
// 更新最大容量
maxArea = Math.max(maxArea, thisArea);
// 移动指针
if(height[i] <= height[j]){
i++;
}else{
j--;
}
}
return maxArea;
};
最后我们再通过图示来证明我们方法的正确性
我们在移动指针的时候抛弃了很多的解,但是这些解是可以抛弃的解,不会影响结果的解
将本题的搜索空间用矩阵表示出来就是这样的【白色区域】
如果遍历所有的解【所有小方块】,就需要的复杂度
通过本题的双指针来缩减搜索空间, 双指针最先得到的解是右上方的解
假设左边的 0 号柱子较短。代码中就是 i++; 对应于搜索空间,就是削减了一行的搜索空间,如下图所示。
排除掉了搜索空间中的一行之后,我们再看剩余的搜索空间,仍然是倒三角形状。
最终的动图如图所示
图片作者:nettee; 链接
最后,欢迎关注我的专栏,和YK菌做好朋友~