- 题目描述:四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
- C++编程实现:
排序+双指针
和三数之和一样,本题的难点依旧在于如何去除重复解
取两个数组合,将问题转化为三数之和
算法流程:
特判,对于数组长度n,如果数组为Null或者数组长度小于4,返回[]。
对数组进行排序。
遍历排序后数组:
对于重复元素,跳过,条件:i>0且nums[i]==nums[i−1],避免出现重复解
二次遍历,重复元素跳过,判断重复元素从i后第二个元素开始,所以条件:j−i>1且nums[j]==nums[j−1]
令左指针L=j+1,右指针R=n−1,当L<R时,执行循环:
*当nums[i]+nums[j]+nums[L]+nums[R]==target时,将结果加入res并执行循环,判断左界和右界是否和下一位置重复,以去除重复解。并同时将L,R移到下一位置,寻找新的解
*若和大于0,说明nums[R]太大,R左移
*若和小于0,说明nums[L]太小,L右移
剪枝条件:
对于本题,按照上述流程写下来,可以通过。
我们继续对算法进行剪枝优化
第一次遍历
若nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target,则可以退出,因为最小四数之和大于目标,则不可能存在结果。**注意:**和三数之和的优化条件不同,三数之和中target=0,所以只要nums[i]>0,则可退出,这里则需要更为严格的条件。
若当前值和数组中最大的三个值相加依旧小于目标,nums[i]+nums[n−1]+nums[n−2]+nums[n−3]<target,则continue
第二次遍历
同理,若nums[i]+nums[j]+nums[j+1]+nums[j+2]>target,break
nums[i]+nums[j]+nums[n−1]+nums[n−2]<target,continue
复杂度分析
时间复杂度:O(n3),数组排序O(NlogN),两次遍历数组O(n2),双指针遍历O(n),总体O(NlogN)+O(n2)∗O(n),O(n3)
空间复杂度:O(1)
作者:zhu_shi_fu
链接:https://leetcode-cn.com/problems/4sum/solution/gu-ding-tao-lu-jian-dan-qing-xi-pai-xu-shuang-zhi-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int nsize = nums.size();
if (nsize < 4)
return {};
sort(nums.begin(),nums.end());
vector<vector<int>> retvec;
int l, r;
for (int i=0; i<nsize-3; i++)
{
if(nums[i]+nums[i+1]+nums[i+2]+nums[i+3] > target) break;
if(nums[i]+nums[nsize-3]+nums[nsize-2]+nums[nsize-1] < target) continue;
if(i>0 && nums[i]==nums[i-1]) continue;
for (int j=i+1; j<nsize-2; j++)
{
if(nums[i]+nums[j]+nums[j+1]+nums[j+2] > target) break;
if(nums[i]+nums[j]+nums[nsize-2]+nums[nsize-1] < target) continue;
if(j>i+1 && nums[j]==nums[j-1]) continue;
l = j+1;
r = nsize-1;
while (l<r)
{
int sumn = nums[i] + nums[j] + nums[l] + nums[r];
if (sumn < target)
l++;
else if(sumn > target)
r--;
else
{
retvec.push_back({nums[i], nums[j], nums[l], nums[r]});
while(l<r && nums[l] == nums[++l]);
while(l<r && nums[r] == nums[--r]);
}
}
}
}
return retvec;
}
};
重要的是剪枝条件要考虑周全,才能保证舍去不必要的循环。