一、题目描述------盛水最多的容器
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明: 你不能倾斜容器。
示例 1:

css
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
解释: 图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
css
输入: height = [1,1]
输出: 1
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
二、题解(双指针)
没错 哥们又超时了(反面案例)
js
var maxArea = function(height) {
let ans=0;
for(let i=0;i<height.length;i++){
for(let j=i+1;j<height.length;j++){
ans=Math.max(ans,(j-i)*Math.min(height[i],height[j]));
}
}
return ans;
};
这个解法是O(N2)的时间复杂度,把所有组合遍历了一遍,这里超时了就不再详解了
双指针解法(最优解)
js
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let left = 0; // 左指针
let right = height.length - 1; // 右指针
let maxWater = 0; // 记录最大水量
while (left < right) {
// 计算当前容器的宽度和高度
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
// 计算当前容器的容量
const currentWater = width * minHeight;
// 更新最大水量
maxWater = Math.max(maxWater, currentWater);
// 移动指针:总是移动高度较小的那个指针
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxWater;
};
核心思路
通过双指针逼近,不断寻找更大的容器面积,并最终找到最大值。
-
双指针,界定搜索范围:
left
和right
指针分别指向数组的首尾,它们界定了当前考虑的容器的左右边界。 -
面积计算,比较大小: 在每次循环中,计算
left
和right
指针所形成的容器的面积,并与当前已知的最大面积maxWater
进行比较,更新maxWater
。 -
移动策略,缩小搜索空间: 这是最关键的一点。代码总是移动高度较小的那个指针。 这个策略基于以下观察:
- 容器的面积由宽度和高度决定,而高度是
left
和right
指针指向的两个高度中的较小值。 - 如果移动较高的那个指针,容器的宽度会减小,而高度最多只能保持不变,因此面积不可能增大。
- 相反,如果移动较低的那个指针,虽然宽度会减小,但高度有可能增加,从而有可能找到更大的面积。
- 容器的面积由宽度和高度决定,而高度是
-
迭代逼近,找到最优解: 通过不断地移动指针,逐步缩小搜索范围,从而遍历所有可能的容器,并找到面积最大的那个。
更直观地理解移动策略:
假设 height[left] < height[right]
,这意味着当前容器的面积受限于 height[left]
。 移动 left
指针的目的是:
- 寻找一个更大的
height[left]
,这样新的容器的高度才有可能增加,从而有可能得到更大的面积。 - 即使新的
height[left]
比原来的小,但是如果height[left]
和height[right]
的差值变小,整体的面积也可能变大。
为什么这种方法是有效的?
该算法并非暴力地枚举所有可能的容器(那样的时间复杂度会是 O(n^2)),而是通过一种贪心的策略,每次移动指针都朝着有可能增大面积的方向前进。 最终,它能够保证找到全局的最大面积。
使用双指针来界定容器的边界,并通过每次移动高度较小的指针的方式,贪心地寻找更大的容器面积,最终找到全局最大值。
详细解析
-
let left = 0;
和let right = height.length - 1;
: 这两行初始化了两个指针,left
指针指向数组的开头,right
指针指向数组的结尾。 这两个指针用来定义当前容器的左右边界。 -
while (left < right)
: 这是一个while
循环,只要left
指针还在right
指针的左边,循环就会继续执行。 当left
和right
相遇时,说明已经遍历了所有可能的容器,循环结束。 -
const width = right - left;
: 这行计算当前容器的宽度,即左右指针之间的距离。 -
const minHeight = Math.min(height[left], height[right]);
: 这行计算当前容器的高度,即左右指针指向的高度的较小值。容器的高度由较短的线段决定,因为水只能填充到较短线段的高度。 -
const currentWater = width * minHeight;
: 这行计算当前容器的容量,即宽度乘以高度。 -
maxWater = Math.max(maxWater, currentWater);
: 这行更新maxWater
,如果当前容器的容量大于maxWater
,则将maxWater
更新为当前容量。 -
if (height[left] < height[right]) { left++; } else { right--; }
: 这是移动指针的关键部分。 总是移动高度较小的那个指针。 原因如下:- 如果移动较高的指针,容器的宽度会减小,而高度最多只能保持不变(最坏的情况),因此面积不可能增大。
- 如果移动较低的指针,虽然宽度会减小,但高度有可能增加,从而有可能找到更大的面积。
实例与展示









三、结语
再见!