力扣题目-三数之和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)之后" 才需要触发,目的是消除重复的结果。
```cpp
class Solution {
public:
vector
去重判断:
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,整体循环结束