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.
Note: The solution set must not contain duplicate quadruplets.
For example, given array S = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
题意很简单,这里就不多说了。这里做一个总结:
第一种方法是把KSum降低为K-1Sum,然后逐步降低,最后处理2Sum,这个可以通过递归实现,也可以通过循环实现,最后的2Sum是通过双指针遍历得到,但是要注意避免重复元素的计算。这里有一个小细节需要注意:我们要寻找KSum的解,那么数组遍历范围是[0,len-k+1)或者[0,len-k],所以在降低k值的时候要注意遍历范围的计算;
第二种是通过Hash实现,不过这个是针对4Sum的,针对KSum也可以类似处理,不过实现起来有点麻烦。思想主要就是把可能的2Sum之和存放到map中,然后通过双指针遍历map来实现2Sum+2Sum=4Sum。 这个问题的比较麻烦的地方是要注意处理重复的元素,因为index可能出现重复,index对应的value重复问题,可以在代码中看出,这个处理比较麻烦。这仅仅是4Sum就这么麻烦,要是针对KSum,要是使用Hash实现,那么处理的就更加麻烦了。
综合来说,我建议使用第一种方法,建议循环实现。
建议和这一道题leetcode 454. 4Sum II 一起学习
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Solution
{
/*
* 通过吧KSum降低为K-1Sum,然后逐步降低,最后处理2Sum
*
* */
public List<List<Integer>> fourSumBBBB(int[] nums, int target)
{
Arrays.sort(nums);
return kSum(nums, target, 3);
}
public List<List<Integer>> kSum(int[] nums, int target,int kSum)
{
List<Integer> one=new ArrayList<>();
kSumByDG(kSum, nums, 0, nums.length-kSum+1, target, one);
return finResList;
}
public void kSumByDG(int kSum,int[] nums,int beg ,int end,int target,List<Integer> one)
{
if(kSum==2)
{
int allSum=0;
for(int i=0;i<one.size();i++)
allSum+=one.get(i);
int left=beg,right=end;
while(left < right)
{
int ssum = allSum + nums[left]+nums[right];
if(ssum==target)
{
List<Integer> tmp = new ArrayList<>(one);
tmp.add(nums[left]);
tmp.add(nums[right]);
finResList.add(tmp);
while(left < right && nums[left]==nums[left+1]) left++;
while(left < right && nums[right]==nums[right-1]) right--;
left++;
right--;
}else if(ssum < target)
left++;
else
right--;
}
return ;
}else
{
for(int i=beg;i<end;i++)
{
while(i!=beg && nums[i]==nums[i-1])
continue;
if( i+1 < end+1)
{
one.add(nums[i]);
kSumByDG(kSum-1, nums, i+1, end+1, target, one);
one.remove(one.size()-1);
}
}
}
}
/*
* 通过循环,来实现,和上面通过递归实现的本质是一样的。
* */
public List<List<Integer>> fourSum(int[] nums, int target)
{
List<List<Integer>> finResList=new ArrayList<>();
if(nums==null ||nums.length<4)
return finResList;
Arrays.sort(nums);
for(int i=0;i<nums.length-3;i++)
{
if(i>0 && nums[i]==nums[i-1]) continue;
for(int j=i+1;j<nums.length-2;j++)
{
if(j!=i+1 && nums[j]==nums[j-1]) continue;
int beg=j+1;
int end=nums.length-1;
while(beg < end)
{
int sum = nums[i] + nums[j] + nums[beg] + nums[end];
if(sum==target)
{
List<Integer> t = new ArrayList<Integer>();
t.add(nums[i]);
t.add(nums[j]);
t.add(nums[beg]);
t.add(nums[end]);
finResList.add(t);
while(beg<end && nums[beg+1]==nums[beg])
beg++;
while(beg<end && nums[end-1]==nums[end])
end--;
beg++;
end--;
}else if(sum < target)
beg++;
else
end--;
}
}
}
return finResList;
}
HashMap<Integer, List<List<Integer>>> map=new HashMap<>();
List<List<Integer>> finResList=new ArrayList<>();
List<List<Integer>> finIndexList=new ArrayList<>();
//使用hash需靠考虑的细节实在是太多了,下面的方法并不能覆盖所有情况
public List<List<Integer>> fourSumWithHash(int[] nums, int target)
{
Arrays.sort(nums);
if(nums==null || nums.length<4)
return finResList;
else if(nums.length==4)
{
int sum=0;
List<Integer> tt=new ArrayList<>();
for(int i=0;i<nums.length;i++)
{
tt.add(nums[i]);
sum+=nums[i];
}
if(sum==target)
finResList.add(tt);
return finResList;
}
for(int i=0;i<nums.length;i++)
{
for (int j = i+1; j < nums.length; j++)
{
int sum=nums[i] + nums[j];
List<Integer> tmp=new ArrayList<>();
tmp.add(i);
tmp.add(j);
tmp.add(nums[i]);
tmp.add(nums[j]);
List<List<Integer>> res=map.get(sum);
if(res==null)
{
res=new ArrayList<>();
map.put(sum, res);
}
map.get(sum).add(tmp);
}
}
List<Integer> key=new ArrayList<>(map.keySet());
key.sort(null);
int left=0,right=key.size()-1;
while(left <= right)
{
int sum=key.get(left) + key.get(right);
if(sum==target)
{
List<List<Integer>> res1=map.get(key.get(left));
List<List<Integer>> res2=map.get(key.get(right));
for (int j = 0; j < res1.size(); j++)
{
for (int k = 0; k < res2.size(); k++)
{
AddOne(res1.get(j),res2.get(k));
}
}
left++;
right--;
}else if(sum < target)
left++;
else
right--;
}
return finResList;
}
public void AddOne(List<Integer> a,List<Integer> b)
{
Set<Integer> set=new HashSet<Integer>();
set.add(a.get(0));
set.add(a.get(1));
set.add(b.get(0));
set.add(b.get(1));
if(set.size()<4)
return ;
List<Integer> index=new ArrayList<>(set);
index.sort(null);
boolean need=true;
/*
* 重复添加问题的解决,先判断index的重复
* */
for(int i=0;i<finIndexList.size();i++)
{
List<Integer> tt=finIndexList.get(i);
if(tt.get(0)==index.get(0) && tt.get(1)==index.get(1) && tt.get(2)==index.get(2) && tt.get(3)==index.get(3))
{
need=false;
break;
}
}
if(need)
{
List<Integer> res=new ArrayList<>();
res.add(a.get(2));
res.add(a.get(3));
res.add(b.get(2));
res.add(b.get(3));
res.sort(null);
boolean sub=true;
//index不重复的前提下考虑,具体数值的重复
for(int i=0;i<finResList.size();i++)
{
List<Integer> tt=finResList.get(i);
if(tt.get(0)==res.get(0) && tt.get(1)==res.get(1) && tt.get(2)==res.get(2) && tt.get(3)==res.get(3))
{
sub=false;
break;
}
}
if(sub)
finResList.add(res);
}
}
public static void main(String[] args)
{
Solution so=new Solution();
int []nums={-2,-1,0,0,1,2};
System.out.println(so.fourSum(nums, 0));
}
}
下面是KSum的C++实现,主要是通过把K Sum 降低为k-1 Sum来做的,就是一个递归解决问题。
代码如下:
#include <iostream>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <queue>
#include <stack>
#include <string>
#include <climits>
#include <algorithm>
#include <sstream>
#include <functional>
#include <bitset>
#include <numeric>
#include <cmath>
#include <regex>
using namespace std;
class Solution
{
public:
vector<vector<int>> res;
vector<vector<int>> fourSum(vector<int>& nums, int target)
{
sort(nums.begin(), nums.end());
calaKSum(nums, target, 4);
return res;
}
void calaKSum(vector<int>& a, int tar,int k)
{
vector<int> one;
dfs(a, tar, k, one, 0, a.size() - k + 1);
}
void dfs(vector<int>& a, int tar, int k, vector<int>& one, int beg, int end)
{
if (k == 2)
{
int sum = accumulate(one.begin(), one.end(), 0);
int left = beg, right = end;
while (left < right)
{
int tmp = sum + a[left] + a[right];
if (tmp == tar)
{
vector<int> tmp = one;
tmp.push_back(a[left]);
tmp.push_back(a[right]);
res.push_back(tmp);
while (left < right && a[left] == a[left + 1]) left++;
while (left < right && a[right] == a[right - 1]) right--;
left++;
right--;
}
else if (tmp < tar)
left++;
else
right--;
}
}
else
{
for (int i = beg; i < end; i++)
{
if (i != beg && a[i] == a[i - 1])
continue;
one.push_back(a[i]);
dfs(a, tar, k-1, one, i + 1, end+1);
one.pop_back();
}
}
}
};