问题:
1、给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。(此处重复指三元组重复,而非三元组中的元素,下同)
2、给你一个由 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.将待搜寻中的数组进行排序
2.a指代排序后的数组第一个元素,b指代第二个元素,c指代最后一个元素。(a,b,c均为数组下标)
3.保证a,b不动,因为此时a+b是最小的两个数之和,而c是最大数,如果a+b+c>target,将c左移,即减小c,直到bc相遇;=target即为所求,移动b,进行下一步;<target则说明此时移动c无用,需要移动b,进行下一步。
4.将b右移,但需要移动至下一个不同的数字(因为数组中可能存在多个相同的元素),所以需要确保此时
- b移动了,即b>a+1
- b换了一个数字,num[b]!=num[b-1]
重复3,直到b到达倒数第二个数时,此时下一步需移动a
5.将a右移,处理方式与b相同,直至a到达倒数第三个数。
此处官方将循环条件都设置为了<n,应为a<n-2,b<n-1,c<n,但时间复杂度均为n³,无伤大雅。
具体内容可看15官方解答
https://leetcode.cn/problems/3sum/solution/san-shu-zhi-he-by-leetcode-solution/
代码如下
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
// 枚举 a
for (int first = 0; first < n - 2; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n - 2; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
ans.push_back({nums[first], nums[second], nums[third]});
}
}
}
return ans;
}
};
若是改为三元数组中的元素也不同,则需修改判断条件
从开始就确定a位于一个数的最后位置
即当a<n-2(a最大取到n-2),确保a与后面一个数字不同,若相同则右移a。b在上一问已经确定了移动时必换数字,仅需保证初始时b与a不同即可,a又是所代表数字的最后位置,后一位数字必不同,故ab不同
若使用nums[first] == nums[first - 1],则a已经换数
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if ((first < n - 1) && nums[first] == nums[first + 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1;second < (n-1); ++second) {
// 需要和上一次枚举的数不相同
if ((second > first + 2) && nums[second] == nums[second + 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
ans.push_back({ nums[first], nums[second], nums[third] });
}
}
}
return ans;
}
};
四数之和:
与三数同理,仅需在原c外层多进入一个与ab循环相似的c1,c1<n-1。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// d 对应的指针初始指向数组的最右端
int fourth = n - 1;
int mytarget = target-nums[first]-nums[second];
for(int third=second+1;third<n;++third)
{
if(third>second+1&&nums[third]==nums[third-1]){
continue;
}
// 需要保证 c 的指针在 d 的指针的左侧
while (third < fourth && nums[third] + nums[fourth] > mytarget) {
--fourth;
}
// 如果指针重合,随着 c后续的增加
// 就不会有满足 a+b+c+d=target 并且 c<d 的 d 了,可以退出循环
if (third == fourth) {
break;
}
if (nums[third] + nums[fourth] == mytarget) {
ans.push_back({nums[first], nums[second], nums[third],nums[fourth]});
}
}
}
}
return ans;
}
};
确保四元数组元素不同与上同理
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if ((first <n-3) && nums[first] == nums[first + 1]) {
continue;
}
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// d 对应的指针初始指向数组的最右端
int fourth = n - 1;
int mytarget = target - nums[first] - nums[second];
for (int third = second + 1; third < n; ++third)
{
if (third > second + 1 && nums[third] == nums[third - 1]) {
continue;
}
// 需要保证 c 的指针在 d 的指针的左侧
while (third < fourth && nums[third] + nums[fourth] > mytarget) {
--fourth;
}
// 如果指针重合,随着 c后续的增加
// 就不会有满足 a+b+c+d=target 并且 c<d 的 d 了,可以退出循环
if (third == fourth) {
break;
}
if (nums[third] + nums[fourth] == mytarget) {
ans.push_back({ nums[first], nums[second], nums[third],nums[fourth] });
}
}
}
}
return ans;
}
};