目录
[11. 盛最多水的容器](#11. 盛最多水的容器)
[15. 三数之和](#15. 三数之和)
本篇带来两道力扣高频真题:入门级《盛最多水的容器》+ 进阶级《三数之和》,通俗拆解思路、详解核心逻辑、标注易错点,新手也能一遍看懂,直接收藏刷题~
11. 盛最多水的容器
题目描述
给定一个长度为 n 的整数数组 height,有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。
解题思路
刚接触这道题,很容易想到两层 for 循环暴力枚举,遍历所有组合计算面积,但时间复杂度太高,数据量大时会直接超时。
✅ 最优解:双指针贪心算法
- 初始化左指针指向数组开头 ,右指针指向数组末尾;
- 容器面积由短板决定:面积 = 两指针间距 × 最小高度;
- 核心贪心规则 :每次移动高度更小的指针(只有移动短板,才有可能让容器面积变大);
- 遍历过程中不断更新最大面积,直到两指针相遇。
满分代码
java
运行
class Solution {
public int maxArea(int[] height) {
// 边界值判断:数组为空直接返回0
if(height == null || height.length == 0){
return 0;
}
// 左指针起始位置,右指针指向数组末尾
int left = 0, right = height.length - 1;
// 记录最大盛水量
int maxArea = 0;
// 双指针遍历
while(left < right){
// 计算当前面积:宽度 * 最小高度
int currentArea = (right - left) * Math.min(height[left], height[right]);
// 更新最大面积
maxArea = Math.max(maxArea, currentArea);
// 贪心策略:移动高度较小的指针
if(height[left] < height[right]){
left++;
}else{
right--;
}
}
return maxArea;
}
}
核心关键点
- 时间复杂度:O(n)(仅遍历一次数组)
- 空间复杂度:O(1)(仅使用常数变量)
- 本质:利用贪心思想,舍弃无效枚举,最大化计算效率
15. 三数之和
题目描述
给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k,同时满足 nums[i] + nums[j] + nums[k] == 0。请你返回所有和为 0 且不重复的三元组。
解题思路
这道题是双指针进阶题 ,也是面试必考的去重陷阱题 ,核心难点在于排序 + 双指针 + 严格去重:
- 先排序:对数组排序,方便后续指针移动和去重操作;
- 固定一个数 :遍历数组,将
nums[i]作为第一个数(a); - 双指针找另外两个数 :左指针
left = i+1(b),右指针right = 数组末尾(c); - 指针移动规则:
-
- 三数之和 > 0:右指针左移(减小数值);
- 三数之和 < 0:左指针右移(增大数值);
- 三数之和 = 0:记录结果,同时去重,移动指针;
- 重中之重:去重逻辑(90% 的人都在这里出错):
-
- 对第一个数
a去重:跳过重复的nums[i]; - 对
b、c去重:找到结果后,跳过连续重复的左右指针元素。
- 对第一个数
满分代码
java
运行
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
// 核心第一步:数组排序,为双指针和去重做准备
Arrays.sort(nums);
int len = nums.length;
// 遍历固定第一个数字 a
for(int i = 0; i < len; i++){
// 剪枝优化:排序后第一个数大于0,不可能凑出和为0
if(nums[i] > 0){
return result;
}
// 对 a 去重:跳过重复元素,避免结果集重复
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
// 定义双指针:left指向i+1,right指向数组末尾
int left = i + 1;
int right = len - 1;
// 双指针查找 b 和 c
while(left < right){
int sum = nums[i] + nums[left] + nums[right];
// 三数之和过大,右指针左移
if(sum > 0){
right--;
}
// 三数之和过小,左指针右移
else if(sum < 0){
left++;
}
// 找到满足条件的三元组
else{
// 将结果加入集合
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 对 b 去重:跳过连续重复的left
while(left < right && nums[left] == nums[left+1]){
left++;
}
// 对 c 去重:跳过连续重复的right
while(left < right && nums[right] == nums[right-1]){
right--;
}
// 找到结果后,同时移动双指针
left++;
right--;
}
}
}
return result;
}
}
易错点详解(必看)
- a 的去重为什么用 nums[i] == nums[i-1]****? 如果用
nums[i] == nums[i+1],会直接跳过合法的三元组,导致结果丢失; - 排序的作用:不仅方便指针移动,更是去重的前提;
- 剪枝优化:排序后第一个数大于 0,直接终止循环,提升效率。
刷题总结
- 盛最多水的容器 :双指针贪心入门题,核心是移动短板,无复杂边界处理;
- 三数之和 :双指针进阶题,核心是排序 + 双指针 + 三层去重,吃透去重逻辑就掌握了这道题;
- 双指针技巧:适用于有序数组 / 需要枚举配对的场景,是优化暴力解法的首选方案。