给你一个由 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]]
思路:
四数之和,和三数之和是一个思路,都是使用双指针法, 基本解法就是在三数之和的基础上再套一层for循环。
但是有一些细节需要注意,例如: 不要判断nums[k] > target
就返回了,三数之和 可以通过 nums[i] > 0
就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。比如:数组是[-4, -3, -2, -1]
,target
是-10
,不能因为-4 > -10
而跳过。但是我们依旧可以去做剪枝,逻辑变成nums[i] > target && (nums[i] >=0 || target >= 0)
就可以了。
三数之和的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下标作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。
那么一样的道理,五数之和、六数之和等等都采用这种解法。
对于三数之和双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。
之前我们讲过哈希表的经典题目:四数相加II,相对于本题简单很多,因为本题是要求在一个集合中找出四个数相加等于target,同时四元组不能重复。
而四数相加II是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于本题还是简单了不少!
本题代码如下:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int left, right;
vector<vector<int>>result;
if (nums.size() < 4) { return result; }
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size() - 3; i++)
{
if (nums[i] > (target/4 + 1)) { return result; }//若排序后第一个数就大于目标值的1/4,则直接退出
if (i > 0 && nums[i] == nums[i - 1]) { continue; }//去重操作
for (int j = i + 1; j < nums.size() - 2; j++)
{
if (j > i+1 && nums[j] == nums[j - 1]) { continue; }//去重操作
left = j + 1;
right = nums.size() - 1;
while (left < right)
{
if ((long)nums[i] + nums[j] + nums[left] + nums[right] < target) { left++; }
else if ((long)nums[i] + nums[j] + nums[left] + nums[right] > target) { right--; }
else
{
result.push_back({ nums[i] , nums[j] , nums[left] , nums[right] });
while ((left<right)&&(nums[left] == nums[left + 1])) { left++; }//左指针去重
while ((left < right) &&( nums[right] == nums[right - 1])) { right--; }//右指针去重
left++;//找到满足条件的值,左右指针同时收缩,进行后续的查找
right--;
}
}
}
}
return result;
}
};