目录
问题描述:
给你一个由 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]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
实现代码:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target)
{
vector<vector<int>> result;//用于返回的结果
sort(nums.begin(),nums.end());//排序
for(int k=0;k<nums.size();k++)
{
//剪枝
if(nums[k]>target&&nums[k]>=0)
{
break;
}
//对指针k去重
if(k>0&&nums[k]==nums[k-1])
{
continue;
}
for(int i=k+1;i<nums.size();i++)
{
//剪枝
if(nums[k]+nums[i]>target&&nums[k]+nums[i]>=0)
{
break;
}
//对指针i去重
if(i>k+1&&nums[i]==nums[i-1])
{
continue;
}
int left=i+1;
int right=nums.size()-1;
while(left<right)
{
if((long)nums[k]+nums[i]+nums[left]+nums[right]>target) right--;
else if((long)nums[k]+nums[i]+nums[left]+nums[right]<target) left++;
else
{
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
//left去重
while(left<right&&nums[left]==nums[left+1]) left++;
//right去重
while(left<right&&nums[right]==nums[right-1]) right--;
left++;
right--;
}
}
}
}
return result;
}
};
和三数之和的题思路一样,这里在外层多加一层循环,然后改一改一些代码细节即可。
这里最重要的还是剪枝和去重操作,下面解释一下对应的代码。
对于第一层k的剪枝:
//剪枝
if(nums[k]>target&&nums[k]>=0)
{
break;
}
当nums[k]大于target后,由于前面已经进行过排序了,所以后面不可能再出现让其和为target的数了,至于为什么还要同时判断nums[k]>=0,因为有可能target为负数,当nums[k]已经大于target时,后面任然可以出现相加让其和更小的数,就是两个负数相加,所以这里要判断一下大于等于零。举个例子:nums={-3,-1,0,0},target=-4,当nums[1]=-3>-5时,明显是不能break的,会少收集这种情况。
对于指针k的去重:
//对指针k去重
if(k>0&&nums[k]==nums[k-1])
{
continue;
}
当指针k指向的数与其前一个数相同时,直接continue跳出此次循环,直接进行下一次循环。这里为什么不是与其后一个数相比而是与前一个数相比呢,因为如果是和后一个数相比,会直接在第一次遇到这个数的时候就跳出循环,导致指针i指向这个数的情况没有记录到,所以这里要与前一个数相比。
对于第二层k+i的剪枝:
//剪枝
if(nums[k]+nums[i]>target&&nums[k]+nums[i]>=0)
{
break;
}
和第一层一样的逻辑,这里就不解释了,就是罗列一下。
对于指针i的去重:
//对指针i去重
if(i>k+1&&nums[i]==nums[i-1])
{
continue;
}
和对于指针k的去重逻辑相同。
对于指针left和指针right去重:
//left去重
while(left<right&&nums[left]==nums[left+1]) left++;
//right去重
while(left<right&&nums[right]==nums[right-1]) right--;
当left指向的数与其后一个数相同时,向右移动left。
当right指向的数与其前一个数相同时,想左移动right。
最后left++,right--,使其指向第一个不同的数。
至于为什么不把这个两个去重条件一开始就写在while循环里呢,原因和上面i指针的一样,至少要记录一次然后再判断,全跳过去不就一次都没用这个数了么。