LeetCode18.四数之和
给定一个包含 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]
]
双指针法
四数之和转三数之和,再转两数之和
class Solution {
public:
vector
<vector
<int>> fourSum(vector
<int>& nums
, int target
) {
vector
<vector
<int>> res
;
sort(nums
.begin(), nums
.end());
if (nums
.empty()) return {};
for(int z
= 0; z
< nums
.size(); z
++){
if (z
> 0 && nums
[z
] == nums
[z
- 1]) continue;
int newTarget
= target
- nums
[z
];
for(int k
= z
+1; k
< nums
.size(); k
++){
if(k
> z
+1 && nums
[k
] == nums
[k
- 1]) continue;
int newTarget2
= newTarget
- nums
[k
];
int i
= k
+ 1, j
= nums
.size() - 1;
while (i
< j
) {
if (nums
[i
] + nums
[j
] == newTarget2
) {
res
.push_back({nums
[z
], nums
[k
], nums
[i
], nums
[j
]});
while (i
< j
&& nums
[i
] == nums
[i
+ 1]) ++i
;
while (i
< j
&& nums
[j
] == nums
[j
- 1]) --j
;
++i
; --j
;
} else if (nums
[i
] + nums
[j
] < newTarget2
) ++i
;
else --j
;
}
}
}
return res
;
}
};
回溯法
首先将 nums 升序排序,并把答案四元组中没确定的个数设为 n
我把剪枝分为了 4 类,括号内的是用什么完成剪枝
如果剩余可选的数字数量少于 n,则剪掉(递归返回);每层递归中,从第二轮循环开始,如果数组中当前数字和前一个相同,则剪掉(进行下一次循环,这条的任务就是去重);如果 当前数字 + 已确定数字的和 + (n - 1) * 排序后数组中当前数字的下一个数字 > target,则说明后面的数无论怎么选,加起来都一定大于 target,所以剪枝(递归返回);如果 当前数字 + 已确定数字的和 + (n - 1) * 排序后数组最后一个数字 < target,则说明当前数字不能选,当前数字加后面的数字都一定小于 target ,所以剪枝(进行下一次循环);
cpp版
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std
;
class Solution_18_001 {
private:
vector
<vector
<int>> ans
;
vector
<int> nums
;
vector
<int> path
;
int target
;
int numSize
;
void dfs(int idx
, int sum
) {
if (sum
== target
&& path
.size() == 4) {
ans
.emplace_back(path
);
return;
}
for (int i
= idx
; i
< numSize
; ++i
) {
if (numSize
- i
< int(4 - path
.size()))
return;
if (i
> idx
&& nums
[i
] == nums
[i
- 1])
continue;
if (i
< numSize
- 1 && (sum
+ nums
[i
] + int(3 - path
.size()) * nums
[i
+ 1] )> target
)
return;
if (i
< numSize
- 1 && (sum
+ nums
[i
] + int(3 - path
.size()) * nums
[numSize
- 1]) < target
)
continue;
path
.emplace_back(nums
[i
]);
dfs(i
+ 1, sum
+ nums
[i
]);
path
.pop_back();
}
return;
}
public:
vector
<vector
<int>> fourSum(vector
<int> &nums
, int target
) {
sort(nums
.begin(), nums
.end());
this->nums
= nums
;
this->target
= target
;
this->numSize
= nums
.size();
if (numSize
< 4)
return ans
;
dfs(0, 0);
return ans
;
}
};
int main() {
int arr
[] = {1, 0, -1, 0, -2, 2};
vector
<int> nums(arr
, arr
+ sizeof(arr
) / sizeof(int));
Solution_18_001 s
;
vector
<vector
<int>> vvec
;
vvec
= s
.fourSum(nums
, 0);
vector
<vector
<int>>::iterator vv
= vvec
.begin();
while (vv
!= vvec
.end()) {
vector
<int>::iterator v
= (*vv
).begin();
while (v
!= (*vv
).end()) {
std
::cout
<< *v
<< "\t";
v
++;
}
std
::cout
<< std
::endl
;
vv
++;
}
}
java版
public class Main {
private List
<List
<Integer>> res
;
private int target
;
public List
<List
<Integer>> fourSum(int[] nums
, int target
) {
res
= new ArrayList<>();
Arrays
.sort(nums
);
this.target
= target
;
helper(nums
, 0, 0, new ArrayList<>());
return res
;
}
private void helper(int[] nums
, int index
, int target
, List
<Integer> list
) {
if (list
.size() == 4 && target
== this.target
) {
res
.add(new ArrayList<>(list
));
return;
}
for (int i
= index
; i
< nums
.length
; i
++) {
if (nums
.length
- i
< 4 - list
.size()) return;
if (i
> index
&& nums
[i
] == nums
[i
- 1]) continue;
if (i
< nums
.length
- 1 && (target
+ nums
[i
] + (3 - list
.size()) * nums
[i
+ 1] ) > this.target
) return;
if (i
< nums
.length
- 1 && (target
+ nums
[i
] + (3 - list
.size()) * nums
[nums
.length
- 1] ) < this.target
)
continue;
list
.add(nums
[i
]);
helper(nums
, i
+ 1, target
+ nums
[i
], list
);
list
.remove(list
.size() - 1);
}
}
public static void main(String
[] args
) {
Main s
= new Main();
int[] nums
= {1, 0, -1, 0, -2, 2};
List
<List
<Integer>> list
= s
.fourSum(nums
,0);
System
.out
.println(list
);
}
}
参考
leetcode leetcode.46.全排列——回溯算法+dfs 全排列 ll (Permutations II)——回溯算法+dfs emplace_back() 和 push_back 的区别