思路:
由于结果需要不包含重复的三元组。
「不重复」的本质是什么?我们保持三重循环的大框架不变,只需要保证:
第二重循环枚举到的元素不小于当前第一重循环枚举到的元素;
第三重循环枚举到的元素不小于当前第二重循环枚举到的元素。
也就是说,我们枚举的三元组 (a, b, c)(a,b,c) 满足 a≤b≤c,保证了只有 (a, b, c)(a,b,c) 这个顺序会被枚举到,
而(b, a, c)、(c, b, a)等等这些不会,这样就减少了重复。要实现这一点,我们可以将数组中的元素从小到大进行排序,随后使用普通的三重循环就可以满足上面的要求。
同时,对于每一重循环而言,相邻两次枚举的元素不能相同,否则也会造成重复
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
// 前置条件
if (nums == null || nums.length < 3) {
return new ArrayList<>();
}
// 结果
List<List<Integer>> result = new ArrayList<>();
// 哈希表,记录每个数字在数组中的位置,由于不能重复,所以不用担心数组中有重复数字会对哈希表内容有影响
Map<Integer, Integer> map = new HashMap<>();
int length = nums.length;
// 排序——去重,满足从左到右遍历时,得到的数字a<=b<=c
Arrays.sort(nums);
// 将数字加入哈希表
for (int i = 0; i < length; i++) {
map.put(nums[i], i);
}
int target = 0;
Integer third;
List<Integer> add;
/*
// 为了省去(first>0)这个条件
for (int second = 1; second < length; second++) {
if (second > 1 && nums[second] == nums[second - 1]) {
continue;
}
if ((third = map.get(target - nums[second])) != null) {
if (third > second) {
add = new ArrayList<>();
add.add(nums[0]);
add.add(nums[second]);
add.add(nums[third]);
result.add(add);
}
}
}*/
// 遍历获得的第一个数字
for (int first = 0; first < length; first++) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// 要使三数之和为0,所以目标数为第一个数字取反
target = -nums[first];
// 遍历获得的第二个数字
for (int second = first + 1; second < length; second++) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 尝试从哈希表中获取第三个数字(target - nums[second]),若存在,并且第三个数字需要在第二个数字右侧
if ((third = map.get(target - nums[second])) != null) {
if (third > second) {
// 符合条件,添加进列表中
add = new ArrayList<>();
add.add(nums[first]);
add.add(nums[second]);
add.add(nums[third]);
result.add(add);
}
// 要满足找到的数字a≤b≤c,所以当第二项b>c时,退出当前循环(再继续查找也只会出现b>c,而此组3个数肯定已存在于列表中)
else {
break;
}
}
}
}
return result;
}
}