public List<List<Integer>> threeSum(int[] nums) {
if (nums == null || nums.length < 3) {
return new ArrayList<>();
}
Arrays.sort(nums);
List<List<Integer>> list = new ArrayList<>();
// 对全是0的情况特殊清理
if (nums.length > 2 && nums[0] == 0 && nums[nums.length - 1] == 0) {
list.add(Arrays.asList(0, 0, 0));
return list;
}
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (map.containsKey(0 - nums[i] - nums[j])) {
list.add(Arrays.asList(nums[i], nums[j], -(nums[i] + nums[j])));
}
}
map.put(nums[i], i);
}
// 去重
HashSet set = new HashSet(list);
list.clear();
list.addAll(set);
return list;
}
方法一:使用map和set的处理。新增了对数组元素全部为0的特殊处理,对于全是为0的情况,我们就没必要使用两层for遍历或者使用额外的map空间。
在两层for中,下面的代码用来判断map是否存在0 - a - b的元素(其中a为nums[i]、b为nums[j]),若map中存在则将元素添加到集合中。
1.List<List<Integer>> list = new ArrayList<>()
- list包含两个内部列表,是一个二维的整数列表结构
2.关于Array.sort, 'java.util.Arrays.sort()' 该方法是Arrays类的静态方法,在需要对数组进行排序时,非常的好用
-
排序基本数据类型数组
int[] arr = {5, 2, 9, 1, 5}; Arrays.sort(arr);
-
排序对象数组:可以使用 `Arrays.sort()` 来排序包含自定义对象的数组,但前提是这些对象实现了 `Comparable` 接口,或者你可以为排序提供一个自定义的 `Comparator`。
class Person implements Comparable<Person> { private String name; private int age; // 构造函数和其他方法 @Override public int compareTo(Person otherPerson) { return this.age - otherPerson.age; } } Person[] people = new Person[3]; // 初始化 people 数组 Arrays.sort(people); // 使用 compareTo() 方法排序
-
排序范围:`Arrays.sort()` 可以用于排序整个数组,也可以指定排序的起始索引和结束索引,以排序部分数组。
int[] arr = {5, 2, 9, 1, 5}; Arrays.sort(arr, 1, 4); // 对索引 1 到 3 的元素进行排序(2, 5, 9)
-
排序算法:** `Arrays.sort()` 使用了一种快速排序的变种算法来对数组进行排序。这个算法的时间复杂度为 O(n log n),性能非常好。
-
注意事项:使用 `Arrays.sort()` 会改变原始数组的顺序,如果需要保留原数组,可以先创建一个副本进行排序。
3.Arrays.asList() ,创建的列表是不可变的,它是一个固定大小的列表,不能添加或者删除元素
-
List<String> mutableList = new ArrayList<>(Arrays.asList("元素1", "元素2", "元素3")); /*创建了一个可变的List集合*/
-
List<String> list = new ArrayList<>(); list.addAll(Arrays.asList("元素1", "元素2", "元素3"));
第1个和第2个代码示例都创建了一个可变的 `ArrayList` 列表,并将三个字符串元素 `"元素1"`, `"元素2"` 和 `"元素3"` 添加到列表中。这两种方式都可以用来表示创建一个可变长度的列表。第1个代码示例直接在创建 `ArrayList` 时初始化了元素,而第2个代码示例是先创建一个空的 `ArrayList`,然后通过 `addAll` 方法将元素添加到列表中.
4.关于List/Array/HashSet之间的一些转化
List转HashSet
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>(list);
HashSet转List
Set<String> set = new HashSet<>();
List<String> list = new ArrayList<>(set);
方法二:排序+双指针
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> list = new LinkedList<>();
for (int i = 0; i < nums.length; i++) {
// a去重
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
if (nums[i] + nums[left] + nums[right] < 0) {
left++;
} else if (nums[i] + nums[left] + nums[right] > 0) {
right--;
} else {
list.add(Arrays.asList(nums[i], nums[left], nums[right]));
// b和c去重
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
}
}
}
return list;
}
代码分析:
- 总体思路:
- 首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定义一个下标left 在i+1的位置上,定义下标right 在数组结尾的位置上。
- 依然还是在数组中找到 a,b,c ,使得a + b +c =0,这里相当于 a = nums[i],b = nums[left],c = nums[right]。
- 接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
- 如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
- 去重细节分析:
- 由于排序后的数组可能会出现这种情况[-4, -1, -1, -1, -1, -1, -1, 0, 1, 2],此时的nums[i]=-1, 这时候就没有必要一个接着一个往下判断了,因为下面的情况都是类似的,因此下面的这行代码实际上是对a进行一个去重。而这里还有一个细节就是为什么是上面这种代码的判断方法而不是下面的判断方法?因为假如我们的数组是这种情况【-4,-1,-1,2】,此时的i=1,nums[i]=-1,nums[i+1]=-1,若按照下面方法判断,continue 后会直接让i=2,因此会漏掉【-1,-1,2】这个情况
if (i > 0 && nums[i] == nums[i - 1]) { continue; }
if (i > 0 && nums[i] == nums[i + 1]) { continue; }
- 下面这个是其它的一些细节
- 由于排序后的数组可能会出现这种情况[-4, -1, -1, -1, -1, -1, -1, 0, 1, 2],此时的nums[i]=-1, 这时候就没有必要一个接着一个往下判断了,因为下面的情况都是类似的,因此下面的这行代码实际上是对a进行一个去重。而这里还有一个细节就是为什么是上面这种代码的判断方法而不是下面的判断方法?因为假如我们的数组是这种情况【-4,-1,-1,2】,此时的i=1,nums[i]=-1,nums[i+1]=-1,若按照下面方法判断,continue 后会直接让i=2,因此会漏掉【-1,-1,2】这个情况
while (right > left) {
/*去重复逻辑如果放在这里,0,0,0 的情况,
可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
/*
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
*/
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
else {
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
总结:刷题的同时有很多知识点要补,这题涉及到双指针的细节太多了
参考博客:【LeetCode】详解三数之和15. 3Sum Given an array nums of n integers, are there elements a, b, c in nums