力扣刷题笔记-三数之和

发布于:2025-09-11 ⋅ 阅读:(23) ⋅ 点赞:(0)

力扣题目-三数之和C++实现
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
在这里插入图片描述
分析:
题目要求找到的是不能重复的三元组,为了更好地处理重复的数据,想到先对数据进行排序,因为排序以后重复的数据都挨在一起了,并且在排序之后计算也更加方便,能通过指针的移动来进行控制。
然后,我们通过指针的移动来寻找和为0的三个数,如果其中一个数进行了固定,就只需要用双指针来找剩下的两个数了。
以示例nums = [-1,0,1,2,-1,-4]为例,排序后为[-4,-1,-1,0,1,2]
首先可以固定第一个元素i,去找剩下的两个数,剩下的两个数分为用左指针left和右指针right指向他们。
在这里插入图片描述
当第一个数固定之后,计算三个数的和,
如果总和 > 0:说明数值太大,需要减小总和 → 右指针左移(right–)
如果总和 < 0:说明数值太小,需要增大总和 → 左指针右移(left++)
在这里考虑一种特殊情况,减少计算量,就是如果那个固定的数本来就已经大于0了,那就不用往下走了,因为left和right指向的数肯定都是在i的右边的更大的正数
到了这里还没结束,最重要的来了,就是题目说三元组是不能重复的,那么我们就要进行关键的去重工作,主要分为这三个方面:
固定数的去重
[-4, -1, -1, 0, 1, 2]中,可能会用第一个 -1 当固定数找到 [-1,-1,2],再用第二个 -1 当开头,又找到一次 [-1,-1,2],这就重复了。解决办法就是一开始从左到右遍历数组时,只要当前数和它左边紧邻的数一样,就直接跳过当前数。
左指针位置去重
固定了第一个数,正常寻找过程中left会往右移动,往右移动的话left也可能右边的数也是一样的,所以会造成重复返回。假设数组是 nums = [-1, -1, -1, 0, 1, 2](索引 0、1、2 都是 - 1),初始 left=1(指向第二个-1),right=5(指向最右边那个2),此时nums[0] + nums[1] + nums[5] = -1 + (-1) + 2 = 0 → 组合 [-1,-1,2](有效)。如果不处理重复:直接移动 left 到 2(left++),此时 left=2:
nums[0] + nums[2] + nums[5] = -1 + (-1) + 2 = 0 → 又得到 [-1,-1,2],重复。
解决办法是去判断是否nums[left] == nums[left+1],如果当前left指向数和下一个索引的数值重复,就 left++ 跳过。
右指针位置去重
原理和左指针去重一样,解决办法是判断是否nums[right] == nums[right-1],当前right指向的数和前一个索引的数值重复),就 right-- 跳过。
注意,left 和 right 的去重逻辑,只在 “找到有效组合(sum=0)之后” 才需要触发,目的是消除重复的结果。

class Solution {
public:    
vector<vector<int>> threeSum(vector<int>& nums) {        
vector<vector<int>> result;        
// 先对数组进行排序        
sort(nums.begin(), nums.end());                
// 外层循环:遍历固定数(第一个数)        
// 确保至少有i、left、right三个不同索引        
for (int i = 0; i < nums.size() - 2; i++) {            
// 固定数去重:如果当前数与前一个数相同,跳过            
if (i > 0 && nums[i] == nums[i - 1]) {                
continue;
            }                        
// 初始化双指针            
int left = i + 1;               // 第二个数的指针(左指针)            
int right = nums.size() - 1;    // 第三个数的指针(右指针)                        
// 双指针循环:寻找满足条件的第二个数和第三个数            
while (left < right) {                
int sum = nums[i] + nums[left] + nums[right];                                
if (sum < 0) {                    
// 总和太小,左指针右移(增大数值)                    
left++;
                } else if (sum > 0) {                    
// 总和太大,右指针左移(减小数值)                    
right--;
                } else {                    
// 找到有效三元组,加入结果集                    
result.push_back({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--;
                }
            }
        }                
return result;
    }
};

第一轮循环:i=0(固定数 nums[0] = -4)
在这里插入图片描述

初始指针:left=1(数值 - 1),right=5(数值 2)
第一步:sum = -4 + (-1) + 2 = -3 < 0 → 执行 left++
新指针:left=2(数值 - 1),right=5(数值 2)
第二步:sum = -4 + (-1) + 2 = -3 < 0 → 执行 left++
新指针:left=3(数值 0),right=5(数值 2)
第三步:sum = -4 + 0 + 2 = -2 < 0 → 执行 left++
新指针:left=4(数值 1),right=5(数值 2)
第四步:sum = -4 + 1 + 2 = -1 < 0 → 执行 left++
新指针:left=5(数值 2),right=5(数值 2)
left=5 不小于 right=5 → 本轮结束
第二轮循环:i=1(固定数 nums[1] = -1)
在这里插入图片描述

初始指针:left=2(数值 - 1),right=5(数值 2)
此时sum = -1 + (-1) + 2 = 0(找到有效组合)
记录 [-1,-1,2]
去重判断:
left=2:nums[2] == nums[3](-1 == 0?否)→ 不移动 left
right=5:nums[5] == nums[4](2 == 1?否)→ 不移动 right
执行 left++ 和 right–
新指针:left=3(数值 0),right=4(数值 1)
在这里插入图片描述

此时sum = -1 + 0 + 1 = 0(找到有效组合)
记录 [-1,0,1]
去重判断:
left=3:nums[3] == nums[4](0 == 1?否)→ 不移动 left
right=4:nums[4] == nums[3](1 == 0?否)→ 不移动 right
执行 left++ 和 right–
新指针:left=4(数值 1),right=3(数值 0)
left=4 不小于 right=3 → 本轮结束
第三轮循环:i=2(固定数 nums[2] = -1)
在这里插入图片描述

固定数去重判断:i=2 > 0 且 nums[2] == nums[1](-1 == -1)→ 执行 continue
结果:不初始化指针,直接跳过本轮
第四轮循环:i=3(固定数 nums[3] = 0)
在这里插入图片描述

初始指针:left=4(数值 1),right=5(数值 2)
sum = 0 + 1 + 2 = 3 > 0 → 执行 right–
新指针:left=4(数值 1),right=4(数值 1)
终止:left=4 不小于 right=4 → 本轮结束
i的值为4时不满足循环条件<nums.size()-2,整体循环结束


网站公告

今日签到

点亮在社区的每一天
去签到