给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
方法一:暴力(超出时间限制)
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result=new ArrayList<>();
if(nums.length<4){
return result;
}
Arrays.sort(nums);
System.out.println(Arrays.toString(nums));
int arr[]=new int[4];
fun(nums,arr,0,0,result,target);
return result;
}
//pos是填充到数组中的第几个位置
public void fun(int nums[],int arr[],int pos,int from,List<List<Integer>> list,int target){
if(pos==4){
int sum=0;
// for(int i=0;i<4;i++){
// System.out.print(arr[i]+ " ");
// }
for(int i=0;i<4;i++){
sum+=arr[i];
}
// System.out.println();
while(from>0&&pos==0&&from<nums.length&&nums[from]==nums[from-1]){
from++;
}
if(sum==target){
List<Integer> integers = new ArrayList<>();
integers.add(arr[0]);
integers.add(arr[1]);
integers.add(arr[2]);
integers.add(arr[3]);
if(!list.contains(integers))
list.add(integers);
return ;
}
}else{
for(int i=from;i<nums.length;i++){
arr[pos]=nums[i];
// if(i+1<nums.length)
fun(nums,arr,pos+1,i+1,list,target);
}
}
}
方法二(来自leetcode官方题解):
使用for循环先找出来前两个i,j,然后使用双指针left,right找出来后两个。(i<j<left<right)
要进行进行剪枝。
官方题解代码及其漂亮。
public List<List<Integer>> fourSum2(int[] nums, int target) {
Arrays.sort(nums);
// System.out.println(Arrays.toString(nums));
List result=new ArrayList<List<Integer>>();
if(nums==null||nums.length<4){
return result;
}
//一重循环找到第一个数
for(int i=0;i<nums.length-3;i++){
// System.out.println("i="+i);
if(i>0&&nums[i]==nums[i-1]){
// System.out.println(1);
continue;
}
//最小值大于target,没前途,说明后面的无论哪一种组合都会大于target,直接返回
if((long)nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target){
// System.out.println(2);
return result;
}
//最大值小于target,直接i++
if((long)nums[i]+nums[nums.length-1]+nums[nums.length-2]+nums[nums.length-3]<target){
// System.out.println(3);
continue;
}
for(int j=i+1;j<nums.length-2;j++){
//如果和上一个位置相等,直接跳转下一个位置,该组合已经出现
if(j>i+1&&nums[j]==nums[j-1]){
continue;
}
//最小值大于target,没前途,说明后面的无论哪一种组合都会大于target,直接返回
if((long)nums[i]+nums[j+1]+nums[j+2]+nums[j]>target){
// System.out.println(">>>i,j+"+i+""+j);
//为啥这里不可以直接return? 原因是这里大于只能证明在当前的i的所有的和小于等于target的情况全部出现,不能证明后面,后面的i不会出现小于、
//等于target的情况
break;
// return result;
}
//最大值小于target,后移j
if((long)nums[i]+nums[j]+nums[nums.length-1]+nums[nums.length-2]<target){
continue;
}
// System.out.println("i,j:"+i+" "+j);
//双指针
int left=j+1,right=nums.length-1;
long sum1=nums[i]+nums[j];
long sum=sum1;
while(left<right){
sum=sum1+nums[left]+nums[right];
// System.out.println("i,j,left,right"+" "+nums[i]+" "+nums[j]+" "+nums[left]+" "+nums[right]);
// System.out.println(sum);
if(sum==target){
List integerArrays = new ArrayList<Integer>();
integerArrays.add(nums[i]);
integerArrays.add(nums[j]);
integerArrays.add(nums[left]);
integerArrays.add(nums[right]);
result.add(integerArrays);
left++;
right--;
while(left<right&&nums[left-1]==nums[left])
left++;
while(left<right&&nums[right+1]==nums[right])
right--;
}else if(sum>target){
right--;
}else{
left++;
}
}
}
}
return result;
}
最后说一种错误的方法:就是用第一个双指针left、right找出来两个,然后再用双指针left1、right1再找出来另外两个。(left<left1<right1<right),这样的方法不对,因为某些满足条件的组合可能会达不到的。