4Sum-leetCode-Java
题目描述
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Notes:
Elements in a quadruplet (a,b,c,d) must be in non-descending order.
(ie, a ≤ b ≤ c ≤ d)The solution set must not contain duplicate quadruplets.
Example:
given array S = {1 0 -1 0 -2 2}, and target = 0.
Solution set :
(-1, 0, 0, 1)
(-2, -1, 1, 2)
(-2, 0, 0, 2)
思路
常规思路
将数组排序后,然后从头开始遍历,找到第一个元素nums[i],第二个元素nums[j],剩余的两个的和即为target-nums[i]-nums[j],剩余的两个元素从j+1-nums.length()-1寻找,这里采用两指针,分别指向j+1,length-1,寻找到合适值,就将对应的序列添加到结果中去。
代码如下:
public static List<Integer> add(int[] a,int i,int j,int k,int m){
List<Integer> l=new ArrayList<Integer>();
l.add(a[i]);
l.add(a[j]);
l.add(a[k]);
l.add(a[m]);
return l;
}
public static List<List<Integer>> fourSum(int[] nums, int target) {
int[] num=nums;
Arrays.sort(num);//排序
int len=num.length;
List<List<Integer>> ll=new ArrayList<List<Integer>>();//保存最终结果
for(int i=0;i<len-3;i++){
if(i>0&&num[i]==num[i-1])//避免重复值
continue;
for(int j=i+1;j<len-2;j++){
if(j>i+1&&num[j]==num[j-1])
continue;
int head=j+1,tail=len-1;//定义头指针和尾指针
int target2=target-num[i]-num[j];
while(head<tail){
int temp=num[head]+num[tail];//记录当前和
if(temp<target2)
head++;
else if(temp>target2)
tail--;
else{
List<Integer> list=add(num,i,j,head,tail);//将正确序列加入到链表中
ll.add(list);
int p=head+1;//继续寻找合适的序列,忽略重复值
while(p<tail&&num[head]==num[p]){
p++;
}
head=p;
int q=tail-1;
while(q>head&&num[q]==num[tail]){
q--;
}
tail=q;
}
}
}
}
return ll;
}
降低时间复杂度的思路
我们可以将数组中所有的和找出来,将sum作为Map的key,将和对应的所有的两元组作为value,这里记录的是元素的在数组中的下标位置,而不是元素值。比如:-1,0,0,1,2
建立的map为:
-1:{0,1}{0,2}
0:{0,3}{0,0}
1:{1,2}{1,3}
2:{1,4}{2,4}
3:{3,4}
还是采用上面提到的方法,为了避免重复,我们先对数组进行排序,然后从头开始遍历,先找到一个元素A,再确定一个元素B ,剩下的和为target-A-B,这里我们就直接从我们建立的map中寻找,这里需要注意的是,可能存在重复值,一是:找到map中下标必须大于B的下标;二是:去除map中重复的value值。
public static List<List<Integer>> fourSum(int[] nums,int target){
int[] num=nums;
Arrays.sort(num);
int len=num.length;
List<List<Integer>> res=new ArrayList<List<Integer>>();
Map<Integer,List<int[]>> map=new HashMap<Integer,List<int[]>>();
//建立两个元素和的映射
for(int i=0;i<len-1;i++){
for(int j=i+1;j<len;j++){
int[] temp=new int[2];
temp[0]=i;
temp[1]=j;
int sum=num[i]+num[j];
if(!map.containsKey(sum)){
List<int[]> list=new ArrayList<int[]>();
list.add(temp);
map.put(sum, list);
}else{
List<int[]> list=map.get(sum);
list.add(temp);
map.put(sum, list);
}
}
}
//寻找合适的结果
for(int i=0;i<len-3;i++){
if(i!=0&&num[i]==num[i-1])
continue;
for(int j=i+1;j<len-2;j++){
if(j!=i+1&&num[j]==num[j-1])
continue;
int targetMinus=target-num[i]-num[j];
//直接从map中寻找是否存在对应的键值
if(map.containsKey(targetMinus)){
List<int[]> list=map.get(targetMinus);
boolean flag=false;//记录此次循环是否为第一次添加到结果
for(int p=0;p<list.size();p++){
int[] temp=list.get(p);
//如果小标小于j忽略,这里是为了避免重复
if(temp[0]<=j)
continue;
//将这一次的结果与该循环中上一次添加的结果对比,如果相同则继续下一次循环
if(flag&&res.get(res.size()-1).get(2)==num[temp[0]])
continue;
List<Integer> l=add(num,i,j,temp[0],temp[1]);
res.add(l);
flag=true;
}
}
}
}
return res;
}