双指针巅峰对决:三数之和的去重艺术与盛水容器的贪心博弈
·
1.前言
从“快慢”到“对撞”
如果说快慢指针(26、27题)是在数组上“加工零件”,那么对撞双指针(15、11题)就是在搜索空间里“围追堵截”。它们的本质都是利用数据的特性(有序性或单调性),剔除掉无效的计算路径。
2. 15. 三数之和
2.1分析
首先看到整数数组nums,判断是否存在三元数组,这道题我刚开始的想法就是像之前的滑动窗口一样去便利,但是这样的话时间复杂度就会是O(N^3)需要遍历很多次就会超时.并且需要补充一点的是滑动窗口本质上是一个伸缩的“窗帘”。它处理的对象必须是连续的子数组(Subarray)或连续的子串(Substring)。
那么我就在考虑去使用双指针去做这道题,首先我们就可以排序sort(),这样也可以为后面的去重做铺垫. 我们就可以i遍历for循环,用left指针和right指针进行操作,并且和大往左走,和小往右走,
那接下来看我的画图演示:

这道题有个很关键的难点就是如何去重?
举个例子:
- 外层去重: 数组:[-1, -1, 0, 1, 2],第一轮 i = -1 时, 双指针帮你找到的数组是(-1,0,1), 到第二轮时, i 还是等于 -1 找到的数组肯定与刚才那个相同, 所以我们就需要去重. nums[i] == nums[i-1],直接 continue(跳过),这就是外层去重。
- 内层去重: 注意: 只有当你找到了一组 sum == 0 的答案后,才需要执行这一步。假设固定了 nums[i] = -2,剩下的数组是 [1, 1, 1, 1, 1, 1]。left 指向1 , right指向1 , 找到数组(-2,1,1), 但是下一轮你的left还是1, 就会重复(-2,1,1)!
2.2 上代码
//15. 三数之和
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>>res=new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
//外层的i去重
if (i>0 && nums[i]==nums[i-1]){
continue;
}
int left=i+1;
int right= nums.length-1;
while (left<right){
int sum=nums[i]+nums[left]+nums[right];
if (sum==0){
res.add(Arrays.asList(nums[i],nums[left],nums[right]));
//左边去重
while (left<right && nums[left]==nums[left+1]){
left++;
}
//右边去重
while (left<right && nums[right]==nums[right-1]){
right--;
}
//找到一组答案后,左右一起收缩
left++;
right--;
} else if (sum<0) {
left++;
}else{
right--;
}
}
}
return res;
}
3. 11. 盛最多水的容器
这道题的代码极短,但背后的贪心策略极深。
3.1 思路
这道题的意思是算面积最大,那么我们可以根据题意写出如下公式:
area=(R-L)*Math.min(height[L],hight[R]);
接下来就需要把最大面积更新max
然后本题的重难点来了,我们需要怎么移动L和R指针呢?
核心思想: 谁矮谁移动
L矮的话就L++,R矮的话就R--, 这样我们就可以找最大的啦
3.2 代码
这道题比较简单稍加思考就能相=想出,我就不画图啦,直接上代码:
//11.盛最多水的容器
public int maxArea(int[] height) {
int left=0;
int right= height.length-1;
int res=0;
while (left<right){
//计算出面积
int area=(right-left)*Math.min(height[right], height[left]);
res=Math.max(res,area);
//谁矮谁移动
if (height[left]<height[right]){
left++;
}else{
right--;
}
}
return res;
}
4. 总结
双指针的灵魂
双指针不仅仅是两个 int 变量,它是 “剪枝” 思想的体现。
-
在 15 题中,我们剪掉了那些会导致重复的路径。
-
在 11 题中,我们剪掉了那些“移动长板必减面积”的无效路径。
更多推荐

所有评论(0)