先分享第一反应想到的解法,因为只要求四个元素相加和为一目标值,可以先定下一个值,将问题简化为求三个数和为固定值,然后再固定一个值,转为两个数和为固定值,以此来找到定值为K的四个元素的所有组合。
有一个需要注意的点,避免重复,给定的数组中可能会有相同值的数,为了避免最后得到重复的组合,我们要先将数组排序,这样遇到重复的跳过即可。
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
public class fixedSum{
private static int NUM_AMOUNT=4;
private static List<List<Integer>>fixedSum(int[]nums,int target){
List<List<Integer>>res=new ArrayList<>();
//如果数组长度不足4,返回空结果
if(nums==null||nums.length<NUM_AMOUNT) {
return res;
}
//对数组进行排序
Arrays.sort(nums);
int len=nums.length;
//先找一个数
for(int i=0;i<len-NUM_AMOUNT+1;i++) {
//对于重复值就不再计算
if(i>0&&nums[i]==nums[i-1]) {
continue;
}
//对于连续的四个数相加如果大于TARGET,则之后不可能再出现四元组,跳出循环
if(nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target) {
break;
}
//如果当前值加上最大的三个数都小于target,那就跳出此轮循环
if(nums[i]+nums[len-3]+nums[len-2]+nums[len-1]<target) {
continue;
}
//找第二个数
for(int j=i+1;j<len-NUM_AMOUNT+2;j++) {
if(j>i+1 &&nums[j]==nums[j-1]) {
continue;
}
//连续的三个数相加如果大于target,跳出循环
if(nums[i]+nums[j]+nums[j+1]+nums[j+2]>target) {
break;
}
//加上最大的两个数如果小于target,跳出此轮循环
if(nums[i]+nums[j]+nums[len-2]+nums[len-1]<target) {
continue;
}
//将三数之和问题转化为两数之和,用二分法解决
int l=j+1,r=len-1;
while(l<r) {
int sum=nums[i]+nums[j]+nums[l]+nums[r];
if(sum==target) {
res.add(Arrays.asList(nums[i],nums[j],nums[l],nums[r]));
while(l<r &&nums[l]==nums[l+1])
{
l++;
}
l++;
while(l<r &&nums[r]==nums[r-1]) {
r--;
}
r--;
}
else if(sum<target){
l++;
}
else {
r--;
}
}
}
}
return res;
}
public static void main(String []args) {
Scanner input=new Scanner(System.in);
int t=input.nextInt();
//while(t-->0)
for(int i=0;i<t;i++) {
int n=input.nextInt();
int target=input.nextInt();
int []nums=new int[n];
for(int j=0;j<n;j++) {
nums[j]=input.nextInt();
}
List<List<Integer>>res=fixedSum(nums,target);
print(res);
}
}
private static void print(List<List<Integer>>res) {
for(List<Integer>subArr:res) {
for(Integer element:subArr) {
System.out.print(element+" ");
}
System.out.print("$");
}
System.out.println();
}
}