昨天华为二面的时候碰到了这道题的简化版(两数之和),今天顺序做题的时候就碰到了。不过之前看面经的时候有注意过这个题,所以仔细看看面经是非常重要的。
描述
Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路
暴力解法的时间复杂度太高,去重操作也比较麻烦。所以我们先对数列进行排序,这样可以节省时间消耗。
再一个就是对于结果数列的生成过程中的去重问题。为了方便表述,将三个数设为A、B、C
- 三个数中的最先确定的数(A),如果当前的数字与前一个相同,是不需要加入结果的
- 三个数中的后两个(B、C),如果与自己对应的上一个相同时,也需要继续移动
- 且B、C应该出现在A之后
核心算法是:遍历这个数组,用A指代当前值,B为A后数组的低端,C为A后数组的高端,若三者之和小于0,说明B应该右移,否则是C左移。配合上述的去重即可得到正确答案。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> list = new LinkedList<List<Integer>>();
//遍历数组取A
for(int i = 0; i<nums.length; i++){
//当A出现重复时,就继续下一个
while(i != 0 && nums[i] == nums[i-1] && i<nums.length - 1){
i++;
continue;
}
//设置B、C的初始值
int low = i+1, high = nums.length - 1;
//对每个A进行查找B、C的操作
while(low < high){
//合法值时
if(nums[i] + nums[low] + nums[high] == 0){
List<Integer> temp = new LinkedList<>();
temp.add(nums[i]);
temp.add(nums[low]);
temp.add(nums[high]);
list.add(temp);
//即使下一个也合法,因为重复也不能加入结果
while (low < high && nums[low] == nums[low+1]){
low++;
}
while (low < high && nums[high] == nums[high-1]){
high--;
}
//去完重复时,因为这两个数字要一起移动才能保证B、C不重复
low++;high--;
//不合法时把B、C进行移动
}else if(nums[i] + nums[low] + nums[high] < 0){
low++;
}else{
high--;
}
}
}
return list;
}
}Runtime: 18 ms, faster than 89.80% of Java online submissions for 3Sum.
Memory Usage: 46.7 MB, less than 91.17% of Java online submissions for 3Sum.
相关知识
Java 集合嵌套List of List
List<List<Integer>>类型的创建:直接使用List<List<Integer>> list = new List<List<Integer>>();是错的,因为List是接口,不能实例化
但如果使用
List<List<Integer>> list = new LinkedList<LinkedList<Integer>>();
又会报错(cannot convert from LinkedList<LinkedList<Integer>> to List<List<Integer>>)
正确的做法是修改成:
List<LinkedList<Integer>> list = new LinkedList<LinkedList<Integer>>();
List<List<Integer>> list = new LinkedList<List<Integer>>();
这样才可以,也就是说,泛型的类型参数必须相同。
这样才可以用接口类引用实现类。