1. 对撞指针
1.1 思路
首先对数组排序,时间复杂度 O(nlogn)。大多数避免重复的问题,解决策略都是排序,即对结果施加人为顺序约束。
外层循环,从左向右遍历数组,选定三元组中的第一个数(也是三元组中的最小值)。此处存在两个剪枝:
- 如果最小值大于零,则不可能找到另外两个更大的数,使三数之和为零,直接结束外层循环;
- 避免重复:如果 nums[i] == nums[i-1],则找到的另外两个数的组合一定相同,直接跳过本轮循环。
在 (k, n) 范围内,采用对撞指针查找组合:
- 初值:left = i + 1,right = nums.length() - 1;
- 循环条件:left < right;
- 如果 nums[left] + nums[right] + nums[k] == 0,记录三元组,同时令 left++、right–,注意避免重复;
- 如果 nums[left] + nums[right] + nums[k] < 0,令 left++,注意避免重复;
- 如果 nums[left] + nums[right] + nums[k] > 0,令 right–,注意避免重复;
避免重复策略:遍历过程中,如果 nums[i] == nums[i-1],则直接跳过本轮循环。
1.2 复杂度
时间复杂度:O(n^2)
空间复杂度:O(1)
1.3 代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if (nums == null || nums.length < 3) {
return new ArrayList<>();
}
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int sum = -nums[i];
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
if (left > i + 1 && nums[left] == nums[left - 1]) {
left++;
continue;
}
if (right < nums.length - 1 && nums[right] == nums[right + 1]) {
right--;
continue;
}
if (nums[left] + nums[right] == sum) {
res.add(Arrays.asList(nums[i], nums[left], nums[right]));
left++;
right--;
} else if (nums[left] + nums[right] < sum) {
left++;
} else {
right--;
}
}
}
return res;
}
}