力扣-盛最多水的容器

思路分析

容器的水量 = 「两条线的水平距离」×「两条线中较矮的那条的高度」(短板决定容量)。

暴力枚举所有两条线的组合(O (n²))会超时,最优解法用** 双指针贪心策略** :

  1. 初始指针 left 指向数组开头(0),right 指向数组末尾(n-1),此时水平距离最大;
  2. 计算当前容器水量,更新最大水量;
  3. 贪心移动指针:始终移动指向较矮线条的指针(因为移动高线条只会让水平距离减小,而容量由短板决定,无法得到更大水量;移动矮线条可能遇到更高线条,从而提升容量);
  4. 重复步骤 2-3,直到 left >= right。

代码实现

java 复制代码
public int maxArea(int[] height){
    // 定义双指针,left指向数组的第一个元素,right指向数组的最后一个元素
    int left = 0, right = height.length - 1;
    // 定义maxArea记录最大面积
    int maxArea = 0;
    // 遍历数组,更新maxArea
    while(left < right){
        // 计算当前面积
        int curArea = (right - left) * Math.min(height[left], height[right]);
        // 更新maxArea
        maxArea = Math.max(maxArea, curArea);
        // 更新指针
        if (height[left] < height[right]){
            left++;
        }else{
            right--;
        }
    }
    return maxArea;
}

复杂度分析

  • 时间复杂度:O (n),仅遍历数组一次,双指针移动总次数为 n;
  • 空间复杂度:O (1),仅使用常数额外空间。

为什么这种方式可以

核心结论:双指针通过「贪心策略+排除无效解」,无需遍历所有情况(双层for循环),就能保证找到最大值------因为它每一步都在主动淘汰不可能成为最优解的组合,只保留有潜力的候选,本质是"用逻辑减少无效枚举"。

要理解这个问题,我们先拆透两个关键:

一、先想透:为什么双层for循环能得到答案,但没必要?

双层for循环的核心是「枚举所有两条线的组合」,计算每个组合的水量,再取最大值。这确实能得到正确答案,因为它覆盖了所有可能性。

但问题在于效率,所以,我们需要一种"不枚举所有组合,但能找到最大值"的方法,而双指针就是基于「贪心+排除法」实现的。

二、双指针的核心逻辑:为什么"移动短板"能排除无效解?

我们先明确两个前提:

  1. 容器水量 = 水平距离 × 短板高度(短板是容量的"天花板");
  2. 初始时,双指针在数组两端(left=0,right=n-1),此时水平距离最大------这是所有组合中"距离最长"的候选,是一个重要的基准。

接下来关键思考:当我们计算完当前组合(left, right)的水量后,该移动哪个指针?为什么移动短板,而不是长板?

举个例子:假设当前left指向的高度是3,right指向的高度是5(短板是left)。

  • 如果移动长板(right左移):新的组合是(left, right-1)。此时水平距离减少了1,而短板依然是left(3)(因为right-1的高度无论大于3还是小于3,短板都不会比3更大)。所以新组合的水量 = (距离-1)× 短板(≤3),必然小于原来的水量(距离×3)------这个新组合是"无效解",不可能成为最大值,没必要枚举。
  • 如果移动短板(left右移):新的组合是(left+1, right)。此时水平距离减少了1,但短板可能变大(比如left+1的高度是4),新水量 = (距离-1)×4,有可能比原来的水量更大------这个组合是"有潜力的解",需要保留。

简单说:移动长板只会让水量变小,移动短板才有可能让水量变大。所以每一步移动短板,本质是在"淘汰所有以当前短板为左/右边界的无效组合",这些组合没必要再用双层for循环枚举了。

三、为什么双指针能覆盖所有"有潜力的最优解"?

很多人担心:双指针跳过了很多组合,会不会漏掉最大值?

答案是:不会。因为被跳过的组合,全是"不可能比当前已知最大值更大"的无效解。

我们用一个形象的比喻理解:

  • 所有可能的组合,都可以看作是"以不同left和right为边界的矩形";
  • 初始时,我们有一个"最宽的矩形"(left=0,right=n-1),这是第一个候选;
  • 之后每一步,我们都在"缩小宽度",但通过"移动短板",试图让 高度的提升抵消宽度的减少------从而得到更大的面积;
  • 被跳过的组合(比如移动长板得到的组合),** 都是"宽度变小,高度还不变/变小"的矩形 **,它们的面积必然小于当前候选,不可能成为最大值。

举个具体例子(数组[1,8,6,2,5,4,8,3,7]):

  • 初始组合(0,8):水量=8×1=8(短板是0);
  • 移动短板(left=0→1):新组合(1,8),水量=7×7=49(当前最大值);
  • 此时短板是right=8(高度7),移动right=8→7:新组合(1,7),水量=6×3=18(小于49);
  • 继续移动right=7→6:新组合(1,6),水量=5×8=40(仍小于49);
  • 后续所有组合的水量,都无法超过49------因为要么宽度更小,要么短板更低。

整个过程中,双指针只遍历了n次(O(n)),但每一步都在"锁定更优的候选",没有漏掉任何可能成为最大值的组合。

四、总结:双指针 vs 双层for循环

对比维度 双层for循环 双指针
核心逻辑 枚举所有组合,暴力比较 贪心移动短板,排除无效解
时间复杂度 O(n²)(超时风险) O(n)(高效)
是否漏解 不会(覆盖所有情况) 不会(只淘汰无效解)
本质区别 "穷举所有"找最大值 "逻辑筛选"找最大值

简单说:双层for循环是"大海捞针",双指针是"精准定位"------它用贪心策略证明了"很多针根本没必要捞",从而在不牺牲正确性的前提下,大幅提升效率。

这也是算法设计的核心思想:不是所有问题都需要"遍历所有情况",只要能通过逻辑排除无效解,就能用更高效的方法找到答案

相关推荐
yongui478341 天前
基于卡尔曼滤波的电池荷电状态(SOC)估计的MATLAB实现
开发语言·算法·matlab
D_FW1 天前
数据结构第二章:线性表
数据结构·算法
技术狂人1681 天前
(六)大模型算法与优化 15 题!量化 / 剪枝 / 幻觉缓解,面试说清性能提升逻辑(深度篇)
人工智能·深度学习·算法·面试·职场和发展
CoovallyAIHub1 天前
为你的 2026 年计算机视觉应用选择合适的边缘 AI 硬件
深度学习·算法·计算机视觉
汉克老师1 天前
GESP2025年12月认证C++六级真题与解析(单选题8-15)
c++·算法·二叉树·动态规划·哈夫曼编码·gesp6级·gesp六级
刘立军1 天前
程序员应该熟悉的概念(8)嵌入和语义检索
人工智能·算法
技术狂人1681 天前
(七)大模型工程落地与部署 10 题!vLLM/QPS 优化 / 高可用,面试实战必备(工程篇)
人工智能·深度学习·面试·职场和发展·vllm
im_AMBER1 天前
Leetcode 95 分割链表
数据结构·c++·笔记·学习·算法·leetcode·链表
Boilermaker19921 天前
[算法基础] FooldFill(DFS、BFS)
算法·深度优先·宽度优先