题目描述
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < na、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]]
提示:
1 <= nums.length <= 200-109 <= nums[i] <= 109-109 <= target <= 109
解答(C++)
题目分析
本题要求在给定的整数数组 nums 里找出所有不重复的四元组,这些四元组中的元素之和要等于目标值 target。需要注意以下几点:
- 四元组里的元素索引
a、b、c、d要满足0 <= a, b, c, d < n,并且它们互不相同。 - 若两个四元组的元素一一对应,就认为这两个四元组是重复的,重复的四元组只能返回一次。
- 最终结果可以按任意顺序返回。
示例分析
以输入 nums = [1,0,-1,0,-2,2],target = 0 为例:
-
首先对数组进行排序,得到
[-2, -1, 0, 0, 1, 2]。 -
外层循环选择第一个元素
-2,内层循环选择第二个元素-1。 -
初始化左指针指向
0,右指针指向2。计算-2 + (-1) + 0 + 2 = -1 < 0,说明和太小,将左指针右移。 -
左指针指向另一个
0,计算-2 + (-1) + 0 + 2 = -1 < 0,和仍然太小,继续右移左指针。 -
左指针指向
1,计算-2 + (-1) + 1 + 2 = 0,找到了一个满足条件的四元组[-2, -1, 1, 2],将其加入结果集。然后移动左右指针并去重。 -
继续外层和内层循环,重复上述步骤,直到遍历完所有可能的组合。
通过以上步骤,我们可以找出所有满足条件的不重复四元组。
代码
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> quadruplets;
if (nums.size() < 4) {
return quadruplets;
}
sort(nums.begin(), nums.end());
int length = nums.size();
for (int i = 0; i < length - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}
if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
for (int j = i + 1; j < length - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break;
}
if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
int left = j + 1, right = length - 1;
while (left < right) {
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
quadruplets.push_back({nums[i], nums[j], nums[left], nums[right]});
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
left++;
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
}
return quadruplets;
}
};
代码分析
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
// 用于存储最终找到的四元组
vector<vector<int>> quadruplets;
// 如果数组元素少于 4 个,无法构成四元组,直接返回空结果
if (nums.size() < 4) {
return quadruplets;
}
// 对数组进行排序,方便后续去重和使用双指针法
sort(nums.begin(), nums.end());
// 获取数组的长度
int length = nums.size();
// 第一个数的循环,范围是从 0 到 length - 3
for (int i = 0; i < length - 3; i++) {
// 去重操作,如果当前数和前一个数相同,跳过当前循环
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
// 如果当前数和接下来的三个最小数之和大于目标值,由于数组已排序,后续组合的和只会更大,直接跳出循环
if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}
// 如果当前数和最后三个最大数之和小于目标值,说明当前数太小,继续下一个数
if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
// 第二个数的循环,范围是从 i + 1 到 length - 2
for (int j = i + 1; j < length - 2; j++) {
// 去重操作,如果当前数和前一个数相同,跳过当前循环
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
// 如果当前两个数和接下来的两个最小数之和大于目标值,由于数组已排序,后续组合的和只会更大,直接跳出循环
if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break;
}
// 如果当前两个数和最后两个最大数之和小于目标值,说明当前两个数组合太小,继续下一个数
if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
// 双指针,left 指向 j + 1,right 指向数组末尾
int left = j + 1, right = length - 1;
// 双指针循环,直到 left 大于等于 right
while (left < right) {
// 计算当前四个数的和
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
// 如果和等于目标值,将四元组加入结果集
if (sum == target) {
quadruplets.push_back({nums[i], nums[j], nums[left], nums[right]});
// 去重操作,跳过相同的元素
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
left++;
// 去重操作,跳过相同的元素
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
right--;
} else if (sum < target) {
// 如果和小于目标值,左指针右移,增大和
left++;
} else {
// 如果和大于目标值,右指针左移,减小和
right--;
}
}
}
}
// 返回最终结果
return quadruplets;
}
};
代码逻辑总结
- 边界检查:要是数组元素少于 4 个,就直接返回空结果。
- 排序:对数组进行排序,方便后续去重和使用双指针法。
- 双重循环:借助两层循环来确定前两个数,并且进行去重操作。
- 双指针:针对每一组前两个数,利用双指针
left和right来确定后两个数,从而找到所有满足条件的四元组。 - 去重:在每一层循环和双指针移动时,都进行去重操作,避免结果中出现重复的四元组。