算法思路
三指针i, j, k,在每一轮遍历中,固定i,j和k向中间移动寻找和为0。
所以问题的关键是如何移动这三个指针,避免重复的数组。
对于i,j, k,我们需要与它们上一位已经遍历过的数比较
nums[i - 1]和nums[i]比较,如果相等,i++j - 1和j比较,如果相等,j++k和k + 1比较,如果相等,k--
是不是比较反直觉?为什么不跟下一位比较呢?也就是为什么不能i和i + 1比较,j和j + 1比较,k - 1和k比较?
因为这样会错过正确数字
比如下图,i直接越过了上一个-1,这样就错过了[-1, -1, 2]
while (i < n - 2 && nums[i] == nums[i + 1]) {
i++;
}
比如下图,在找出[-2, 0, 2]后,j也是直接越过了上一个1,而k直接执行k--,导致退出while,错过[-2, 1, 1]
res.add(Arrays.asList(x, nums[j], nums[k]));
do {
j++;
} while (j < k && nums[j] == nums[j + 1]);
do {
k--;
} while (j < k && nums[k] == nums[k - 1]);
所以说,要不停与过去的自己比较,不要急着往前跑,才不会错过将来
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
int n = nums.length;
// 排序,使用重复的数相邻
Arrays.sort(nums);
for (int i = 0; i < n - 2; i++) {
// 如果第一个数都大于0,后两个数也是大于0
if (nums[i] > 0) {
return res;
}
while (0 < i && i < n - 2 && nums[i - 1] == nums[i]) {
i++;
}
int x = nums[i];
int j = i + 1;
int k = n - 1;
while (j < k) {
int sum = x + nums[j] + nums[k];
if (sum == 0) {
res.add(Arrays.asList(x, nums[j], nums[k]));
// 先向右移动指针,再判断是否重复
do {
j++;
} while (j < k && nums[j - 1] == nums[j]);
do {
k--;
} while (j < k && nums[k] == nums[k + 1]);
} else if (sum < 0) {
// 太小了,往大的移动
j++;
} else {
// 太大了,往小的移动
k--;
}
}
}
return res;
}
}