算法分析与设计第6周博客
15. 3Sum
Given an array S of n integers, are there elements a, b, c in S 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.
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
题目的意思很简单,就是给出一组整数,找出其中所有三个和为零的数的集合。
如果使用暴力枚举法的话,那么就是从n个数中取出3个,计算它们的和,看其是否为零,这样的时间复杂度是O(n^3),算是很低的效率了。
如果先进行排序呢,排序的时间复杂度是O(n*log(n)),这样就得到了一个有序的数组,如果我们首先确定一个数A[i],然后去剩下的A[i+1]-A[n-1]中寻找两个数,这两个数的和是-A[i]。这样,就把这个问题由三个数的和变成了两个数的和。如果读者熟悉的话,在一个有序的数组里,寻找两个数的和是否等于某个数,这个算法的时间复杂度是O(n),具体的代码如下:
boolean twoSum(int[] nums, int target) {
for (int i = 0, j = nums.length-1; i < j; ) {
if (nums[i]+nums[j] > target)
--j;
else if (nums[i]+nums[j] < target)
++i;
else
return true;
}
return false;
}
这样,就基本解决这个问题了。还有一个需要注意的细节是,返回的集合中是不能有相同的元素的,所以遇到相同的元素的时候,需要跳过。整体的代码如下:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
int i = 0, j = nums.length-1;
for (i = 0; i < nums.length; ++i) {
if (i > 0 && nums[i] == nums[i-1])
continue;
int s = i+1, e = nums.length-1;
while (s < e) {
if (nums[s]+nums[e]+nums[i] > 0) {
--e;
} else if (nums[s]+nums[e]+nums[i] < 0) {
++s;
} else {
List<Integer> one = new ArrayList<>();
one.add(nums[i]);
one.add(nums[s]);
one.add(nums[e]);
res.add(one);
do {
++s;
} while (s < nums.length && nums[s] == nums[s-1]);
do {
--e;
} while (e > 0 && nums[e] == nums[e+1]);
}
}
}
return res;
}
}
这个算法的时间复杂度右两部分组成,排序(O(n*log(n)),需找三个数的和O(n*n),所以最终的时间复杂度也是O(n^2)。