1. 题目
2. 分析
题目有两个要求:
- i!=j!=k
- 三元组不能重复
暴力的解法是连续三次遍历,为了达成第一个条件,我们需要遍历的时候控制i < j < k;为了达成第二个条件,我们需要当满足条件的ijk进行跳过重复的数,这就需要我们对数组进行排序。
3. 代码
3.1 暴力解法
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//先排序
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i = skip(nums, i)) {
for (int j = i + 1; j < nums.length - 1; j = skip(nums, j)) {
for (int k = j + 1; k < nums.length; k = skip(nums, k)) {
if (nums[i] + nums[j] + nums[k] == 0) {
lists.add(Arrays.asList(nums[i], nums[j], nums[k]));
}
}
}
}
return lists;
}
// 跳过重复的数
public int skip(int[] nums, int i) {
do {
i++;
} while (i < nums.length && nums[i - 1] == nums[i]);
return i;
}
}
时间复杂度o(n^3),提交的结果如下:
3.2 优化
暴力的方法显然没办法解决所有的case,当数据量大了之后o(n^3)的复杂度耗时会很久,那么是否有一个能够o(n^2)的方案呢?要从n^3变成n^2,即将n^2 变成 n,我们可以考虑使用双指针的方法:
- 排序后,固定一个i从0开始遍历,当nums[i]>0时,中断流程
- 将l r 双指针分别指向i+1和nums.length - 1
- 记录s 为 nums[i] + nums[l] + nums[r]
- 当 s > 0,r左移
- 当 s < 0, l右移
- 当 s = 0, 记录三元组,且跳过所有的重复数
这样子我们的时间复杂度就变成n^2了
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i - 1] == nums[i]) {
continue;
}
int l = i + 1;
int r = nums.length - 1;
while (l < r) {
int s = nums[i] + nums[l] + nums[r];
if (s > 0) {
r--;
} else if (s < 0) {
l++;
} else {
lists.add(Arrays.asList(nums[i], nums[l], nums[r]));
int temp = nums[r];
do {
r--;
} while (r > l && nums[r] == temp);
temp = nums[l];
do {
l++;
} while (r > l && nums[l] == temp);
}
}
}
return lists;
}
}
4. 总结
这题最开始没有想到可以使用双指针来将n^2的时间复杂度变成n。记忆一下,后续做这种题目的时候可以考虑一下使用双指针