题目:
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组
思路:
- 最基本的做法就是三重for循环暴力解法,但是这样时间复杂度为
O(n^3)
,太慢了 - 我们可以使用排序+双指针的方式降低时间复杂度
- 首先我们先将数组按照从小到大的顺序排序
- 第一重循环还是遍历第一个元素
i
,并设置一个target=-nums[i]
,也就是后两个元素的和应该等于target
才能保证三数之和为0 - 第二重循环遍历第二个元素
j
,与此同时,在第二个元素的遍历过程中,我们将第三个元素k
从后向前遍历,因为数组元素是递增的,如果第二个元素过大,那么就要让第三个元素变小,才能保证两数之和等于target
,且他是从结尾元素开始的,因此要向前走 - 如果我们碰到随着一个元素的增大会导致另一个元素的缩小,那么就可以尝试使用双指针来做
- 这样一来,第二个元素的遍历和第三个元素的遍历其实是同步进行的,再加上第一个元素的一重遍历,整个时间复杂度就变成了:
O(n^2)
以下为代码+注释,其中还有一些细节:
public List<List<Integer>> threeSum(int[] nums) {
// 双指针
List<List<Integer>> res = new ArrayList<List<Integer>>();
int len = nums.length;
// 将数组元素按照从小到大排序
Arrays.sort(nums);
for(int i = 0; i < len; i++){
// 如果当前首元素和上一个相同,因为要求不能出现重复的三元组,所以直接进行下一次循环,
if(i > 0 && nums[i] == nums[i - 1])
continue;
// 设置后两个元素之和要达到的目标值
int target = -nums[i];
// 设置第三个元素的指针从尾部开始走
int k = len - 1;
for(int j = i + 1; j < len; j++){
//保证当前元素和上一个元素值不相同,因为不能出现重复三元组
if(j != i + 1 && nums[j] == nums[j - 1])
continue;
// 如果当前后两个元素的和大于target,需要让第三个元素向前移动找小的值
// 因为数组是增序排列的
while(j < k && nums[j] + nums[k] > target)
k--;
// 如果走到第二个元素和第三个元素重合了都没找到和等于target的,说明后续的值都大于target
// 第二个元素j再向后走也没用了,和第三个元素的和只会更大,直接跳出循环
if(j == k)
break;
// 如果后两个元素和为target,说明该情况符合,加入集合
if(nums[j] + nums[k] == target){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
res.add(list);
}
}
}
return res;
}
笔者也在不断学习,如有错误,欢迎指正!