从倒水问题到盛最多水的容器:一道经典的双指针应用题|LeetCode 11 盛最多水的容器
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)。
最直观的解法:暴力枚举法
最容易想到的方法就是:尝试所有可能的容器组合,找出能盛水最多的那个。就像我们实际准备容器时,可能会挨个试一试每种组合。
具体步骤是这样的:
- 使用两层循环,遍历所有可能的左右边界组合
- 对每个组合,计算其能容纳的水量
- 更新最大水量
让我们用一个小例子来模拟这个过程:
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;
}
优化解法:双指针法
仔细思考,我们其实不需要尝试所有组合。关键是理解:容器的容积取决于两个因素:
- 两边中较短的那条边(决定了水的高度)
- 两边的距离(决定了水的宽度)
如果我们从最宽的容器开始,逐渐向内收缩,每次都移动较短的那条边,就一定不会错过最大容积。
双指针法的原理
为什么这样做是对的?假设我们有两个指针left和right:
- 容积由较短的边决定
- 如果移动较长的边,宽度减小,而高度最多只能是较短边的高度
- 所以移动较长的边,容积一定会减小
- 但移动较短的边,可能会找到一个更高的边,容积可能增大
算法步骤(伪代码)
- 初始化左右指针指向数组两端
- 当左右指针未相遇时:
- 计算当前容积
- 更新最大容积
- 移动较短的那条边的指针
- 返回最大容积
示例运行
让我们用一个例子[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),因为只需要常数级的额外空间。
题目模式总结
这道题体现了两个重要的算法思想:
- 双指针技巧:通过移动两个指针来解决问题
- 贪心思想:每次都移动较短的边,期望找到更好的解
这种模式在很多问题中都有应用,比如:
- 两数之和(排序数组)
- 三数之和
- 接雨水
解决这类问题的通用思路是:
- 观察问题中的单调性质
- 寻找可以优化的搜索策略
- 证明优化策略的正确性
小结
通过这道题,我们不仅学会了如何找到能盛最多水的容器,更重要的是理解了如何用双指针技巧来优化搜索过程。这种思维方式在很多算法问题中都能派上用场。
记住,解决算法问题时,不要满足于暴力解法,多思考是否存在更优雅的方案。有时候,看似复杂的问题,找到正确的思路后,解法会变得异常简单!
作者:忍者算法
公众号:忍者算法
从倒水问题到盛最多水的容器:一道经典的双指针应用题|LeetCode 11 盛最多水的容器的更多相关文章
- LeetCode 11. 盛最多水的容器(Container With Most Water)
题目描述 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .画 n 条垂直线,使得垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两 ...
- Java实现 LeetCode 11 盛最多水的容器
11. 盛最多水的容器 给定 n 个非负整数 a1,a2,-,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) ...
- 力扣Leetcode 11. 盛最多水的容器
盛最多水的容器 给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找 ...
- Leetcode 11.盛最多水的容器 By Python
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两条线, ...
- LeetCode 11 - 盛最多水的容器 - [双指针暴力]
题目链接:https://leetcode-cn.com/problems/container-with-most-water/description/ 给定 n 个非负整数 $a_1,a_2,\cd ...
- [LeetCode]11. 盛最多水的容器(双指针)
题目 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两 ...
- leetcode 11盛水最多的容器
class Solution { public: int maxArea(vector<int>& height) { //双指针法:从最宽的容器开始计算,当更窄的容器盛水量要大于 ...
- LeetCode:盛最多水的容器【11】
LeetCode:盛最多水的容器[11] 题目描述 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 ...
- 11.07图论水题Test
11.07图论水题Test 题目 描述 做法 \(BSOJ6378\) 在\(i\)位置可以到\(i+a_i\)或\(i+b_i\)求\(1\rightarrow n\)字典序最小路径 判可达性后贪心 ...
- 【LeetCode】盛最多水的容器【双指针+贪心 寻找最大面积】
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两条线, ...
随机推荐
- 3、oracle内存讲解
oracle数据库实例(instance) 数据库打开以后,会生成一个内存结构和一堆进程 内存和进程:就是oracle的实例instance oracle数据库实例结构: 用户是通过连接实例来访问数据 ...
- 通过Java代码发送OutLook邮件
准备 我们想通过Java代码实现发送OutLook邮件,必须准备以下材料: OutLook邮箱 目标邮箱 查看OutLook邮箱信息 打开OutLook邮箱,在Settings中搜索或找到SMTP: ...
- .NET9 - 新功能体验(一)
被微软形容为"迄今为止最高效.最现代.最安全.最智能.性能最高的.NET版本"--.NET 9已经发布有一周了,今天想和大家一起体验一下新功能. 此次.NET 9在性能.安全性和功 ...
- Deque的应用案例-回文检查
7.Deque的应用案例-回文检查 回文检测:设计程序,检测一个字符串是否为回文. 回文:回文是一个字符串,读取首尾相同的字符,例如,radar toot madam. 分析:该问题的解决方案将使用 ...
- Java日志手机号脱敏工具类
背景 在开发过程中,很容易将用户敏感信息,例如手机号码.身份证等,打印在日志平台.为了保护用户数据,又不影响日志的打印,需要将日志中的敏感信息进行脱敏. 效果 没看明白,强烈建议 pull项目,执行一 ...
- Natasha v9.0 为 .NET 开发者提供 [热执行] 方案.
项目简介 自 Natasha v9.0 发布起,我将基于 Natasha 的推出热执行方案,这项技术允许基于 控制台(Console) 和新版 Asp.net Core 架构的项目在运行中动态重编译, ...
- Vue.js 组件注册
1.前言 本节讲述组件和2.x版本和3.x版本的注册方式 2.全局注册 2.x版本直接调用Vue.component()方法进行全局注册,所有Vue实例都能使用,包括其组件 //组件代码省略 var ...
- LeetCode题集-5 - 最长回文子串(一)
题目:给你一个字符串 s,找到 s 中最长的回文子串. 这一题作为中等难度,常规解法对于大多数人应该都没有难度.但是其中也有超难的解决办法,下面我们就一起由易到难,循序渐进地来解这道题. 01.暴力破 ...
- AO SDK安装问题
ao sdk for .net安装时,需要进行验证.net框架,没装vs的时候会提示 arcobjects SDK for the Microsoft.NT Framework requires a ...
- VTK 视角的旋转、平移、缩放
在CAD/CAM软件中,都需要旋转.平移和缩放视角,来观察操作图形.由于VTK定义的交互的类型不是很适用,所有通过定义一套自己的交互方式. 在下面代码中,鼠标左键平移,滚轮缩放,右键旋转. 先定义一个 ...