LeetCode 11 盛最多水的容器

点此看全部题解 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中)

生活中的算法

你有没有遇到过这样的场景:家里要举办派对,需要准备一个大容器来调制果汁。面前有很多不同高度的挡板,你需要选择两个,前后固定住,这样就能盛放饮料了。挡板的高度不同,间距也可以调整,你要怎么选择才能盛放最多的饮料呢?

这就是我们今天要讲的"盛最多水的容器"问题。本质上,我们要在一排不同高度的"墙"中选择两堵墙,使它们之间能容纳最多的水。

问题描述

LeetCode第11题"盛最多水的容器"是这样描述的:给定一个长度为n的整数数组height,有n条垂线,第i条线的两个端点是(i, 0)和(i, height[i])。找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。

比如,输入height = [1,8,6,2,5,4,8,3,7],最大容积是49(由height[1]=8和height[8]=7构成,宽度为7)。

最直观的解法:暴力枚举法

最容易想到的方法就是:尝试所有可能的容器组合,找出能盛水最多的那个。就像我们实际准备容器时,可能会挨个试一试每种组合。

具体步骤是这样的:

  1. 使用两层循环,遍历所有可能的左右边界组合
  2. 对每个组合,计算其能容纳的水量
  3. 更新最大水量

让我们用一个小例子来模拟这个过程:

height = [1,8,6,2]

尝试所有组合:
(0,1): min(1,8) * 1 = 1
(0,2): min(1,6) * 2 = 2
(0,3): min(1,2) * 3 = 3
(1,2): min(8,6) * 1 = 6
(1,3): min(8,2) * 2 = 4
(2,3): min(6,2) * 1 = 2 最大容积 = 6

这种思路可以用Java代码这样实现:

public int maxArea(int[] height) {
int maxVolume = 0; // 遍历所有可能的容器组合
for (int i = 0; i < height.length; i++) {
for (int j = i + 1; j < height.length; j++) {
// 计算当前组合的容积
// 容积 = 两边较矮一边的高度 * 两边的距离
int volume = Math.min(height[i], height[j]) * (j - i);
maxVolume = Math.max(maxVolume, volume);
}
} return maxVolume;
}

优化解法:双指针法

仔细思考,我们其实不需要尝试所有组合。关键是理解:容器的容积取决于两个因素:

  1. 两边中较短的那条边(决定了水的高度)
  2. 两边的距离(决定了水的宽度)

如果我们从最宽的容器开始,逐渐向内收缩,每次都移动较短的那条边,就一定不会错过最大容积。

双指针法的原理

为什么这样做是对的?假设我们有两个指针left和right:

  1. 容积由较短的边决定
  2. 如果移动较长的边,宽度减小,而高度最多只能是较短边的高度
  3. 所以移动较长的边,容积一定会减小
  4. 但移动较短的边,可能会找到一个更高的边,容积可能增大

算法步骤(伪代码)

  1. 初始化左右指针指向数组两端
  2. 当左右指针未相遇时:
    • 计算当前容积
    • 更新最大容积
    • 移动较短的那条边的指针
  3. 返回最大容积

示例运行

让我们用一个例子[1,8,6,2,5,4,8,3,7]模拟这个过程:

初始状态:left=0(高度1), right=8(高度7)
容积=min(1,7)*8=8,移动left left=1(高度8), right=8(高度7)
容积=min(8,7)*7=49,移动right left=1(高度8), right=7(高度3)
容积=min(8,3)*6=18,移动right ...依此类推

Java代码实现

public int maxArea(int[] height) {
int maxVolume = 0;
int left = 0;
int right = height.length - 1; while (left < right) {
// 计算当前容积
int width = right - left;
int currentHeight = Math.min(height[left], height[right]);
int volume = width * currentHeight; // 更新最大容积
maxVolume = Math.max(maxVolume, volume); // 移动较短的一边
if (height[left] < height[right]) {
left++;
} else {
right--;
}
} return maxVolume;
}

暴力法vs双指针法

让我们比较这两种解法:

暴力法的时间复杂度是O(n²),需要遍历所有可能的组合。它的优点是直观易懂,适合用来理解问题。但在处理大规模数据时效率较低。

双指针法的时间复杂度是O(n),只需要遍历一次数组。它通过巧妙的证明,保证了不会错过最优解,同时大大提高了效率。

两种方法的空间复杂度都是O(1),因为只需要常数级的额外空间。

题目模式总结

这道题体现了两个重要的算法思想:

  1. 双指针技巧:通过移动两个指针来解决问题
  2. 贪心思想:每次都移动较短的边,期望找到更好的解

这种模式在很多问题中都有应用,比如:

  • 两数之和(排序数组)
  • 三数之和
  • 接雨水

解决这类问题的通用思路是:

  1. 观察问题中的单调性质
  2. 寻找可以优化的搜索策略
  3. 证明优化策略的正确性

小结

通过这道题,我们不仅学会了如何找到能盛最多水的容器,更重要的是理解了如何用双指针技巧来优化搜索过程。这种思维方式在很多算法问题中都能派上用场。

记住,解决算法问题时,不要满足于暴力解法,多思考是否存在更优雅的方案。有时候,看似复杂的问题,找到正确的思路后,解法会变得异常简单!


作者:忍者算法
公众号:忍者算法

从倒水问题到盛最多水的容器:一道经典的双指针应用题|LeetCode 11 盛最多水的容器的更多相关文章

  1. LeetCode 11. 盛最多水的容器(Container With Most Water)

    题目描述 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .画 n 条垂直线,使得垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两 ...

  2. Java实现 LeetCode 11 盛最多水的容器

    11. 盛最多水的容器 给定 n 个非负整数 a1,a2,-,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) ...

  3. 力扣Leetcode 11. 盛最多水的容器

    盛最多水的容器 给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找 ...

  4. Leetcode 11.盛最多水的容器 By Python

    给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两条线, ...

  5. LeetCode 11 - 盛最多水的容器 - [双指针暴力]

    题目链接:https://leetcode-cn.com/problems/container-with-most-water/description/ 给定 n 个非负整数 $a_1,a_2,\cd ...

  6. [LeetCode]11. 盛最多水的容器(双指针)

    题目 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两 ...

  7. leetcode 11盛水最多的容器

    class Solution { public: int maxArea(vector<int>& height) { //双指针法:从最宽的容器开始计算,当更窄的容器盛水量要大于 ...

  8. LeetCode:盛最多水的容器【11】

    LeetCode:盛最多水的容器[11] 题目描述 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为  ...

  9. 11.07图论水题Test

    11.07图论水题Test 题目 描述 做法 \(BSOJ6378\) 在\(i\)位置可以到\(i+a_i\)或\(i+b_i\)求\(1\rightarrow n\)字典序最小路径 判可达性后贪心 ...

  10. 【LeetCode】盛最多水的容器【双指针+贪心 寻找最大面积】

    给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两条线, ...

随机推荐

  1. 19、解析2_1(链、chunk、锁)

    解析 shared pool 图解: library cache里面,暂时可以认为存储着: 1.SQL以及对应的执行计划(所占空间比较小): 2.存储过程.函数.触发器.包,它们编译后的对象(所占空间 ...

  2. Cygwin:windows下的Linux系统

    Cygwin是啥?Cygwin是一个可原生运行于Windows系统上的POSXI兼容环境. 是的,我又开新专辑了<零基础swoole学习笔记>.不是我太贪心,而是最近半年和小伙伴一直在用s ...

  3. 从Hbase shell理解列式存储

    列存储和行存储在理解上的差别挺大,特别是在非常数据行存储之后. 在行存储中,每张表的结构是固定的,某一列可以没有值但是这一列是必须在的.那么可以理解行存储的数据是结构化的. 但是列存储确有每行的数据却 ...

  4. .NET Core 锁(Lock)底层原理浅谈

    CPU原子操作 原子操作,指一段逻辑要么全部成功,要么全部失败.概念上类似数据库事物(Transaction). CPU能够保证单条汇编的原子性,但不保证多条汇编的原子性 那么在这种情况下,那么CPU ...

  5. Datadog发布云成本现状报告:83%的容器支出被闲置资源浪费

    原文链接:https://www.datadoghq.com/state-of-cloud-costs/ 编译:CloudPilot AI 尽管灵活多样的云服务为云成本优化提供了诸多机会,但企业在提升 ...

  6. 对 .NET 开发者来说,Azure AD 改名为 Microsoft Entra ID 意味着什么?

    对 .NET 开发者来说,Azure AD 改名为 Microsoft Entra ID 意味着什么? 原文地址:https://devblogs.microsoft.com/dotnet/azure ...

  7. 企架布道:中电金信应邀出席2023佛山敏捷之旅暨DevOps Meetup

    近日,2023佛山敏捷之旅暨DevOps Meetup活动顺利举行,本次活动以助力大湾区金融和互联网企业敏捷DevOps实施和效能提升为主题,共设立 2个会场,16个话题分享,200余位金融.互联网企 ...

  8. [转]使用Eclipse创建一个简单的servlet项目

    参考链接: 1.使用Eclipse创建一个简单的servlet项目 2.如何使用eclipse创建简单的servlet

  9. Python中的zip/unzip:像拉拉链一样组合数据的艺术

    今天让我们一起探讨Python中一个优雅而强大的内置功能: zip 和 unzip .听名字就知道,它就像我们衣服上的拉链一样,能把两边的数据完美地咬合在一起. 从一个有趣的例子开始 想象你正在开发一 ...

  10. Ubuntu 添加多用户和Samba

    USERNAME="$1" SMBFILE="/etc/samba/smb.conf" if [ $# != 1 ] then echo "使用方: ...