【LeetCode Hot100 刷题日记(5/100)】11.盛最多水的容器——双指针、数组、贪心、面积最大化💧🌊

118 阅读8分钟

📌 题目链接leetcode.cn/problems/co…

🔍 难度:中等 | 🏷️ 标签:数组、双指针、贪心、面积最大化

⏱️ 目标时间复杂度:O(n)

💾 空间复杂度:O(1)


🎯 本题是「双指针」思想的经典应用之一,常出现在面试中考察你对贪心策略与边界推理能力的理解。虽然看似简单,但背后蕴含着极强的逻辑严谨性——为什么只能移动较短的柱子?为什么不能回退?

🔍 本文将从问题建模 → 算法设计 → 正确性证明 → 代码实现 → 面试拓展五个维度深入解析这道题,帮助你在日常复习时快速回顾核心逻辑,轻松应对面试中的变种考题!


🧠 一、题目分析:如何“盛水”?

给定一个长度为 n 的整数数组 height,每个元素代表一根垂直于 x 轴的柱子的高度。

我们要从中选出两条柱子,使得它们与 x 轴构成的“容器”能容纳最多的水。

⚠️ 容器的容量由两个因素决定:

  • ✅ 两柱之间的距离(宽度)
  • ✅ 两条柱子中较短的一根的高度(短板效应)

👉 所以最大水量 = min(height[i], height[j]) * (j - i),其中 i < j

🎯 目标:在所有可能的 (i, j) 组合中,找到使该表达式最大的那一对。

屏幕截图 2025-11-16 200208.png

屏幕截图 2025-11-16 200232.png


🚀 二、核心算法:双指针法(Two Pointers)⚡

✅ 为什么用双指针?

我们从最宽的情况开始(左端点 + 右端点),然后逐步缩小范围,尝试寻找更大的面积。

❓ 为什么不暴力枚举所有组合?
因为暴力解法的时间复杂度是 O(n²),而本题可以利用贪心+单调性优化到 O(n)!

✅ 关键洞察:面积由“短板”和“宽度”共同决定,宽度只会减小,所以必须靠提升“短板”来争取更大面积。


🔁 指针移动策略

初始化:

left = 0, right = n - 1

每一步计算当前面积,并移动较矮的那一侧的指针

if (height[left] < height[right])
    left++;
else
    right--;

💡 为什么只移动较矮的?这是整个算法的灵魂所在!


🧪 三、正确性证明:为何不能回退?🤔

假设当前状态为:

            i'                  i                   j                   j'
            a[i']               a[i]                a[j]                a[j']

设当前面积为:

s = min(a[i], a[j]) × (j - i)

现在考虑是否应该移动 ij

🔄 情况一:a[i] <= a[j]

我们移动 ii'(即左指针右移)

新面积:

s' = min(a[i'], a[j]) × (j - i')

由于 a[i] <= a[j],所以 min(a[i], a[j]) = a[i]

又因为 a[i'] >= a[i](不一定,但我们可以讨论极限情况)—— 实际上我们不知道 a[i'] 是大还是小,但我们知道:

⚠️ 宽度 (j - i') 一定小于 (j - i),所以如果高度不变或下降,面积必然减少。

但关键是:如果我们不移动较矮的柱子,就永远无法突破当前瓶颈!

📌 结论:只有通过移动较矮的柱子,才有可能遇到更高的柱子,从而提升“短板”,弥补宽度损失。


🧩 数学推导验证

令:

  • 当前面积:s = min(h[i], h[j]) * (j - i)
  • 假设 h[i] < h[j],我们考虑移动 ii+1

新面积:

s' = min(h[i+1], h[j]) * (j - i - 1)

虽然宽度减少了 1,但如果 h[i+1] > h[i],那么 min(h[i+1], h[j]) 可能大于原来的 h[i],从而整体面积可能更大!

✅ 所以必须移动较矮的一边,否则永远无法获得更优解!


🔄 为什么不能回退?

比如:我们已经把 left 移动到了某个位置,发现面积变小了,能不能再往回走?

不行!

🧠 原因在于:一旦我们放弃了一个位置,它所参与的所有组合都已经被排除了。而由于我们是从外向内收缩,且每次只移动较矮的一侧,这意味着我们不会重复访问已处理过的区域。

📌 更重要的是:这个过程是不可逆的贪心决策,基于以下事实:

h[left] < h[right],则无论 right 向左移动多少步,h[left] 都是当前左边的最小值,因此不可能再形成比当前更大的面积(除非右边出现更高柱子)。

所以:只要移动了,就不回头!


📦 四、完整代码实现(C++ + 测试)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

// 双指针法
class Solution {
public:
    int maxArea(vector<int>& height) {
        int n = height.size();
        int left = 0, right = n - 1; // 初始化左右指针
        int ret = 0; // 记录最大面积
        
        while (left < right) {
            // 计算当前左右指针构成的容器面积
            int ans = min(height[left], height[right]) * (right - left);
            ret = max(ret, ans); // 更新最大面积
            
            // 移动指向较短垂直线的指针
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return ret;
    }
};

//测试
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    
    // 测试用例1:[1,8,6,2,5,4,8,3,7] 期望输出:49
    vector<int> height1 = {1,8,6,2,5,4,8,3,7};
    Solution solution;
    cout << "测试用例1 [1,8,6,2,5,4,8,3,7] 的结果:" << solution.maxArea(height1) << "\n";
    
    // 测试用例2:[1,1] 期望输出:1
    vector<int> height2 = {1,1};
    cout << "测试用例2 [1,1] 的结果:" << solution.maxArea(height2) << "\n";
    
    return 0;
}

📊 五、算法分析

项目内容
时间复杂度O(n) —— 每个元素最多被访问一次,左右指针总共移动 n 次
空间复杂度O(1) —— 只使用了常量级额外空间
适用场景适用于“两端收缩”、“面积/体积最大化”、“找最优区间”等问题

✅ 这是一种典型的 “从两边向中间逼近” 的双指针模式,常见于:

  • 最大/最小面积
  • 三数之和
  • 两数之和(有序数组版)
  • 滑动窗口(变体)

🎯 六、面试重点 & 高频考点 💼

1. ❓ 问:为什么一定要移动较短的柱子?能否证明其正确性?

✅ 回答模板:

“因为面积由短板和宽度决定。当我们将较短的柱子向内移动时,虽然宽度减少,但存在机会遇到更高的柱子,从而提升短板高度。反之,如果移动较高的柱子,新的短板仍受限于原短板,且宽度减少,面积必然下降。因此,只有移动较短的柱子才有可能获得更大面积。”

“此外,该策略保证了我们在每一步都做出局部最优选择,最终收敛到全局最优解,属于一种贪心 + 单调性的结合。”


2. ❓ 问:有没有可能漏掉最优解?

✅ 回答:

“不会。因为我们从最宽的位置开始,逐步缩小搜索空间,且每次移动都是基于‘不能让短板变得更短’的原则。由于任何未被访问的组合都会包含至少一个已经被跳过的柱子,而这些柱子要么高度不够,要么已被淘汰,所以不会影响最终结果。”


3. ❓ 问:能否用暴力方法解决?有什么缺点?

✅ 回答:

“可以,但时间复杂度为 O(n²),对于大数据集会超时。而且没有体现算法思维的优化意识,容易被面试官质疑‘有没有更好的办法’。”


4. ❓ 问:这题和“三数之和”有什么关系?

✅ 回答:

“两者都用了双指针技术,但目的不同。‘盛水’是求最大面积,而‘三数之和’是求特定和值。前者依赖贪心思想,后者依赖排序+夹逼搜索。”


🔄 七、扩展思考:变形题 & 应用场景

变形题思路
盛最多雨水(接雨水)类似思路,但需要维护最高点
最大矩形面积(直方图)使用栈,但也可以用双指针辅助
最大子数组和不适用,需动态规划
最长连续递增子序列可用滑动窗口

💡 提示:双指针的本质是“降低搜索空间”,适合用于:

  • 有序数组
  • 区间问题
  • 和/差/积/面积最优化

✅ 八、总结:记住这几点!

  1. 🧱 短板效应:面积由最短边决定。
  2. 🔁 只移短板:永远不要移动较高的那一侧。
  3. 🔄 不可回退:指针只能向前,不能后退。
  4. 📈 宽度递减:所以必须靠提高高度来补偿。
  5. 🎯 贪心 + 单调性:是本题的核心数学基础。

🌟 本期完结,下期见!🔥

👉 点赞收藏加关注,新文更新不迷路。关注专栏【算法】LeetCode Hot100刷题日记,持续为你拆解每一道热题的底层逻辑与面试技巧!

💬 欢迎留言交流你的解法或疑问!一起进步,冲向 Offer!💪


📣 下一期预告:LeetCode 热题 100 第6题 —— 三数之和(中等)

🔹 题目:给定一个包含 n 个整数的数组 nums,判断是否存在三个元素 a, b, c ,使得 a + b + c = 0?找出所有满足条件且不重复的三元组。

🔹 核心思路:先排序,然后固定一个数,用双指针查找另外两个数,避免重复。

🔹 考点:双指针、排序、去重、枚举优化。

🔹 难度:中等,是面试中最常见的“多指针”问题之一,务必掌握!

💡 提示:去重是难点!注意三层循环的剪枝与重复判断!


📌 记住:当你在刷题时,不要只看答案,要像写这篇文章一样,深入思考每一步背后的原理、优化空间和面试价值。这才是真正提升算法能力的方式!


📌 一起冲冲冲!🚀