题目
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.
我的想法
没啥想法
解答
jiuzhang solution:
先算twoSum,再for loop第三个数
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length < 3) {
return res;
}
Arrays.sort(nums);
for(int i = 0; i < nums.length - 2; i++) {
if(i > 0 && nums[i] == nums[i-1]) {
continue;
}
twoSum(res, nums, i + 1, -nums[i]);
}
return res;
}
private void twoSum(List<List<Integer>> res, int[] nums, int start, int target) {
int end = nums.length - 1;
while(start < end) {
if(nums[start] + nums[end] == target) {
List<Integer> triple = new ArrayList<>();
triple.add(nums[start]);
triple.add(nums[end]);
triple.add(-target);
res.add(triple);
start++;
end--;
while(start < end && nums[start] == nums[start-1]) {
start++;
}
while(start < end && nums[end] == nums[end+1]) {
end--;
}
} else if(nums[start] + nums[end] < target) {
start++;
} else {
end--;
}
}
}
}
2020.07.19 update
首先想到了利用twoSum来做,但是第一次的写法会超时。
- 每次传回一个
List<List<Integer>
消耗了不必要的内存,并且又要重新遍历一遍list,影响速度。 - 直接用Set规避重复case,做了许多无用的计算
- 对于10w个0的case进行了太多无用计算
class Solution {
//[-4,-1,-1,0,1,2] 6
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
Set<List<Integer>> res = new HashSet<>();
for (int i = 0; i < nums.length - 1; i++) {
List<List<Integer>> lists = twoSum(
Arrays.copyOfRange(nums, i + 1, nums.length),
0 - nums[i]);
if (!lists.isEmpty()) {
for (List<Integer> list : lists) {
list.add(nums[i]);
res.add(list);
}
}
}
List<List<Integer>> resList = new ArrayList<>(res);
return resList;
}
private List<List<Integer>> twoSum(int[] nums, int target) {
int L = 0, R = nums.length - 1;
List<List<Integer>> ret = new LinkedList<>();
while (L < R) {
if (nums[L] + nums[R] == target) {
List<Integer> res = new LinkedList<>();
res.add(nums[L]);
res.add(nums[R]);
ret.add(res);
}
if (nums[L] + nums[R] > target) {
R--;
} else {
L++;
}
}
return ret;
}
}
第二种方法,考虑传入res的地址,并且使用do...while
来跳过重复的case。但对于重复情况的考虑还是不周全,因此还是要用Set来规避重复项
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
Set<List<Integer>> res = new HashSet<>();
for (int i = 0; i < nums.length - 1; i++) {
twoSum(Arrays.copyOfRange(nums, i + 1, nums.length),
0 - nums[i],
res);
}
List<List<Integer>> resList = new ArrayList<>(res);
return resList;
}
private void twoSum(int[] nums, int target, Set<List<Integer>> ret) {
int L = 0, R = nums.length - 1;
while (L < R) {
if (nums[L] + nums[R] == target) {
List<Integer> res = new LinkedList<>();
res.add(nums[L]);
res.add(nums[R]);
res.add(-target);
ret.add(res);
do {
L++;
} while(L + 1 < nums.length && nums[L] == nums[L + 1] && nums[L] == nums[R]);
//nums[L] == nums[R]跳过的情况非常局限,而且指跳了L
} else if (nums[L] + nums[R] > target) {
R--;
} else {
L++;
}
}
}
}
第三种方法。把sort后的数组自己在纸上写出来之后发现,对于相同的target,重复的数都可以跳过。比如:
[-5, -4, -1, 0, 1, 1, 1, 2, 2, 4, 4]
target是5的情况下,只需要输出第一个1和最后一个4,其他的1和4都可以跳过。在使用双指针跳过重复项的时候特别需要注意,用当前的跟上一个状态进行比较,而不是用当前的对下一个状态进行比较而进行跳过(这样容易出错)
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < nums.length - 1; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
twoSum(Arrays.copyOfRange(nums, i + 1, nums.length),
0 - nums[i],
res);
}
return res;
}
private void twoSum(int[] nums, int target, List<List<Integer>> ret) {
int L = 0, R = nums.length - 1;
while (L < R) {
if (nums[L] + nums[R] == target) {
List<Integer> res = new LinkedList<>();
res.add(nums[L]);
res.add(nums[R]);
res.add(-target);
ret.add(res);
do {
L++;
} while(L < R && nums[L] == nums[L - 1]);
do {
R--;
} while(L < R && nums[R] == nums[R + 1]);
} else if (nums[L] + nums[R] > target) {
R--;
} else {
L++;
}
}
}
}