给你一个包含n个整数的数组nums,判断nums中是否存在三个元素a,b,c,使得a+b+c=0?请你找出所有满足条件且不可重复的三元组。
答案中不可以包含重复的三元组。
暴力解法
直接三个循环,遍历所有可能性,判断a+b+c=0,是的话记录。如何去除重复的三元组?用set记录三元组。相同三元组即可被去除。
对于Arrays和泛型需要再学习一遍。
/**
* 暴力:超出时间限制
* @param nums
* @return
*/
private List<List<Integer>> directlySolution(int[] nums) {
if (nums == null || nums.length <= 2) {
return Collections.emptyList();
}
Arrays.sort(nums);
Set<List<Integer>> result = new LinkedHashSet<>();
for (int i = 0; i < nums.length; i++) {
for (int j = i+1; j < nums.length; j++) {
for (int k = j+1; k < nums.length; k++) {
if (nums[i] + nums[j] + nums[k] == 0) {
List<Integer> value = Arrays.asList(nums[i], nums[j], nums[k]);
result.add(value);
}
}
}
}
return new ArrayList<>(result);
}
Hash解法
对于每一个a,去寻找是否存在b+c=-a。对于b,如果没有在Map中有-c-a,就将b存入Map。如果找到相应的b和c,将a,b,c放入set集合以去重。
相比于暴力解法,三重解法变为两重,不如双指针法优化。在于可以更加熟悉Set Map以及泛型的使用。
import java.util.*;
import static java.lang.Math.min;
public class leetcode {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
String str = scan.nextLine();
int[] nums = new int[n];
for(int i = 0; i < n; i++) {
nums[i] = scan.nextInt();
}
List result = threeSum(nums);
System.out.println(result);
}
public static List<List<Integer>> threeSum(int[] nums) {
//如果nums数组数量不足3个,则返回空
if(nums.length <= 2) return Collections.emptyList();
Set<List<Integer>> result = new HashSet<>();
for(int i = 0; i < nums.length-2; i++) {
Map<Integer,Integer> hashmap = new HashMap<>();
for(int j = i+1; j < nums.length; j++) {
int target = -nums[i]; //b+c=target
//以b为基准,判断集合中是否存在target-b
Integer c = target - nums[j];
if(hashmap.containsKey(c)) {
//存在这样的三元组,放入Set
List<Integer> value = Arrays.asList(nums[i],nums[j],c);
value.sort(Comparator.naturalOrder());
result.add(value);
}
else {
//不存在,需要把b存入集合
hashmap.put(nums[j],nums[j]);
}
}
}
return new ArrayList<>(result);
}
}
这样的解法会超出时间限制,如果数组为[0,0,0,...,0]。因此必须用双指针法来优化。
双指针法
对数组线排序,排序后固定一个数nums[i],再使用左右指针指向nums[i]后的两端,数字分别为nums[L]和nums[R],计算三者是否满足0
- 如果nums[i]>0,则三数之和必不为0,结束循环
- 如果nums[i]==nums[i-1],说明数字重复,会导致结果重复,跳过
- 当sum0时,nums[L]nums[L+1]会导致结果重复,L++
- 当sum0时,nums[R]nums[R-1]会导致结果重复,R--
import java.util.*;
import static java.lang.Math.min;
public class leetcode {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
String str = scan.nextLine();
int[] nums = new int[n];
for(int i = 0; i < n; i++) {
nums[i] = scan.nextInt();
}
List result = threeSum(nums);
System.out.println(result);
}
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
int len = nums.length;
if(nums == null || len < 3) return ans;
Arrays.sort(nums);
for(int i = 0; i<len; i++) {
if(nums[i] > 0) break;
if(i>0 && nums[i] == nums[i-1]) continue;
int L = i+1;
int R = len-1;
while (L < R) {
int sum = nums[i] + nums[L] + nums[R];
if(sum == 0) {
ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
while (L < R && nums[L] == nums[L+1]) L++;
while (L < R && nums[R] == nums[R-1]) R--;
L++;
R--;
}
else if (sum < 0) L++;
else if (sum > 0) R--;
}
}
return ans;
}
}