在数组有序的情况下,参考两数之和,使用双指针找到三元组。 对我来说,难点在于如何去重;
详细解释:
由于输入数组可能包含重复元素,直接遍历可能会得到重复的答案。例如,数组 [0,0,0,0],如果不去重,可能会得到多个 [0,0,0]。
去重主要分为三处:第一层循环(第一个数),第二层循环(第二个数),第三层循环(第三个数)。具体如下:
-
第一个数的去重(外层循环)
if (i > 0 && nums[i] == nums[i - 1]) continue;- 目的:跳过当前数与前一个数相同的情况,避免以同样的数为起点重复计算。
- 原理:排序后,相同的数会相邻。如果前一个数已经作为第一个数处理过,当前数就不需要再处理。
-
第二、第三个数的去重(双指针部分)
当找到一个三元组后,需要跳过后续可能重复的元素:
while (left < right && nums[left] == nums[left + 1]) left++; while (left < right && nums[right] == nums[right - 1]) right--;- 目的:跳过和当前
left或right指针所指元素相同的后续元素。 - 原理:如果当前
left或right与下一个元素相同,说明下一个三元组会和当前三元组重复,因此需要直接跳过。
完整流程:
- 找到一个三元组后,先移动
left和right跳过重复元素,然后再移动到下一个不同的元素,继续查找。
- 目的:跳过和当前
整体代码java版:
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);//排序,方便后续判断指针位置
List<List<Integer>> list = new ArrayList<List<Integer>>();
for(int i =0;i<nums.length;i++){
int target = -nums[i];//三数之和,先标记target,从而拆分为两数之和
int left = i+1,right = nums.length-1;
if(i>0&&nums[i]==nums[i-1]){//第一个数去重
continue;
}
while(left<right){
List<Integer> list1 = new ArrayList<Integer>();
if(nums[left]+nums[right]==target){
list1.add(nums[i]);
list1.add(nums[left]);
list1.add(nums[right]);
list.add(list1);
//双指针部分去重
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
}else if(nums[left]+nums[right]>target){
right--;
}else{
left++;
}
}
}
return list;
}
}