给定一个包含 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] ]
解题思路: 初识此题也是百思不得其解,想要继续使用递归回溯的方法,看到评论才发现超时了。。。暴力解法过于暴力不够灵活,于是发现了一种排序+双指针的方法,也就是使用a,b,c,d四个数,for循环a和b,然后在循环中,不断改变c,d的位置,c初始位置是b的后一位,d初始位置是最后一位,中间的去重也很精妙,是通过判断左右两边是否有重复数字,然后跳过的方法,这样避免了使用相同的结果,官方的解题思路我也贴出来:
四数之和与前面三数之和的思路几乎是一样的,嗝。(刚好前些天才写了三数之和的题解) 如果前面的三数之和会做了的话,这里其实就是在前面的基础上多添加一个遍历的指针而已。 会做三数之和的可以不用看下面的了。。
使用四个指针(a<b<c<d)。固定最小的a和b在左边,c=b+1,d=_size-1 移动两个指针包夹求解。 保存使得nums[a]+nums[b]+nums[c]+nums[d]==target的解。偏大时d左移,偏小时c右移。c和d相 遇时,表示以当前的a和b为最小值的解已经全部求得。b++,进入下一轮循环b循环,当b循环结束后。 a++,进入下一轮a循环。 即(a在最外层循环,里面嵌套b循环,再嵌套双指针c,d包夹求解)。 准备工作: 因为要使用双指针的方法,排序是必须要做der~。 时间复杂度O(NlogN). 解决重复解: 上面的解法存在重复解的原因是因为移动指针时可能出现重复数字的情况。所以我们要确保移动指针后, 对应的数字要发生改变才行哦。 时间复杂度: a遍历O(N)里嵌套b遍历O(N)再嵌套c,d双指针O(N)–> O(N^3)。 总比暴力法O(N^4)好些吧。。
c++ 实现代码
class Solution{ public: vector<vector<int>> fourSum(vector<int>& nums, int target) { // 排序 sort(nums.begin(),nums.end()); // 结果的集合 vector<vector<int> > res; if(nums.size()<4){ return res; } int a,b,c,d,_size = nums.size(); for(a = 0; a <= _size - 4; a ++){// a 是外包围 if(a > 0 && nums[a] == nums[a - 1]) continue; //确保nums[a] 改变了 for(b = a + 1; b <= _size - 3; b++){ if(b > a + 1 && nums[b] == nums[b - 1])continue; //确保nums[b] 改变了 c= b + 1,d= _size - 1; while(c < d){ if(nums[a] + nums[b] + nums[c] + nums[d] < target) c ++;//不够c右移 else if(nums[a] + nums[b] + nums[c] + nums[d] > target) d --;//多了d左移 else{ res.push_back({nums[a],nums[b],nums[c],nums[d]}); while(c < d && nums[c + 1] == nums[c]) //确保nums[c] 改变了 c++; while(c < d && nums[d - 1] == nums[d]) //确保nums[d] 改变了 d--; c++; d--; } } } } return res; } };