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:
- Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
- 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)
方法一:
//基本的brute force方法:在eclipse上运行是可以的,也满足三元组中的非降序
//及无重复三元组的要求。但是提示超时,不accept。算法复杂度为O(n^3)
public class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(nums.length < 3) return result;
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){
Integer[] array = {nums[i], nums[j], nums[k]};
Arrays.sort(array); //对三元组排序
List<Integer> list = new ArrayList<Integer>();
list.addAll(Arrays.asList(array)); //addAll方法参数只能为collection
if(!result.contains(list)){ //能够达到去除重复三元组的效果
result.add(list);
}
}
}
}
}
return result;
}
}
方法二:
/引入二分查找,要求返回数组元素值而非index,故可先排序,再找元素
//理论上,算法复杂度为O(n^2*logn),在eclipse上执行也可以(没细对比,可能有误)
//提示超时,不accept
public class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(nums.length < 3) return result;
Arrays.sort(nums);
for(int i=0; i<nums.length; i++){
for(int j=i+1; j<nums.length; j++){
int head = j + 1;
int tail = nums.length - 1;
int needNum = -(nums[i]+nums[j]);
while(head <= tail){ //二分查找
int middle = (head+tail) / 2;
if(nums[middle] == needNum){
List<Integer> list = new ArrayList<Integer>();
list.add(nums[i]);
list.add(nums[j]); //已经排序过了,无需再排序了
list.add(nums[middle]);
if(!result.contains(list)){
result.add(list);
}
break; //二分查找到结果之后,一定要break,否则head=tail时会无限循环
}else if(nums[middle] > needNum){
tail = middle - 1;
}else if(nums[middle] < needNum){
head = middle + 1;
}
}
}
}
return result;
}
}
方法三:(accept)
//排序花费O(nlogn),遍历花费O(n^2),总复杂度为O(n^2)
public class Solution {
//主调方法
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(nums.length < 3) return result;
Arrays.sort(nums);
for(int i=nums.length-1; i>=2; i--){ //每次取nums[i],然后在nums[0~i-1]中寻找两和为-nums[i]的
if(i<nums.length-1 && nums[i]==nums[i+1]){ //遇到重复的nums[i]就跳过
continue;
}
//方法调用,找sum为-nums[i]的pair
List<List<Integer>> tempCollection = twoSum(nums, i-1, -nums[i]);
for(int j=0; j<tempCollection.size(); j++){
List<Integer> tempList = tempCollection.get(j);
tempList.add(nums[i]); //把二元组扩充为三元组
result.add(tempList);
}
}
return result;
}
//被threeSum调用的辅助方法,思想类似于昨天twoSum中的夹逼思想,但是要注意跳过重复的元素
//下面的方法中,参数nums是应经排序过的数组,故调用此方法只需要O(n)的复杂度
private List<List<Integer>> twoSum(int[] nums, int end, int target) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(nums.length < 2) return null;
int left = 0;
int right = end;
while(left < right){ //夹逼的思想
if(nums[left]+nums[right] == target){
List<Integer> list = new ArrayList<Integer>();
list.add(nums[left]);
list.add(nums[right]);
result.add(list);
left++;
right--;
while(left<right && nums[left]==nums[left-1]){ //跳过重复的元素
left++;
}
while(left<right && nums[right]==nums[right+1]){ //跳过重复的元素
right--;
}
}else if(nums[left]+nums[right] > target){
right--;
}else if(nums[left]+nums[right] < target){
left++;
}
}
return result;
}
}