题目链接
题目描述
给定一个无重复元素的正整数数组 candidates
和一个正整数 target
,找出 candidates
中所有可以使数字和为目标数 target
的唯一组合。
candidates
中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target
的唯一组合数少于 150
个。
示例 1:
输入: candidates = [2,3,6,7],
target = 7
输出: [[7],[2,2,3]]
示例 2:
输入: candidates = [2,3,5],
target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2],
target = 1
输出: []
示例 4:
输入: candidates = [1],
target = 1
输出: [[1]]
示例 5:
输入: candidates = [1],
target = 2
输出: [[1,1]]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate
中的每个元素都是独一无二的。1 <= target <= 500
解题思路
方法一:回溯
回到本题,我们定义递归函数 dfs(target, combine, idx) 表示当前在 candidates 数组的第 idx 位,还剩 target 要组合,已经组合的列表为 combine。递归的终止条件为 target <= 0 或者 candidates 数组被全部用完。那么在当前的函数中,每次我们可以选择跳过不用第 idx 个数,即执行 dfs(target, combine, idx + 1)。也可以选择使用第 idx 个数,即执行 dfs(target - candidates[idx], combine, idx),注意到每个数字可以被无限制重复选取,因此搜索的下标仍为 idx。
更形象化地说,如果我们将整个搜索过程用一个树来表达,即如下图呈现,每次的搜索都会延伸出两个分叉,直到递归的终止条件,这样我们就能不重复且不遗漏地找到所有可行解:
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
len = candidates.size();
dfs(candidates,0,target);
return res;
}
private:
vector<vector<int>> res;
vector<int> path;
int len = 0;
void dfs(vector<int>& candidates,int idx,int target){
if(idx==len||target<0){
return;
}
if(target==0){
res.push_back(path);
return;
}
dfs(candidates,idx+1,target);
if(target-candidates[idx]>=0){
path.push_back(candidates[idx]);
dfs(candidates,idx,target - candidates[idx]);
path.pop_back();
}
}
};
class Solution {
List<List<Integer>> res = new LinkedList<List<Integer>>();
List<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
recur(candidates, target, 0);
return res;
}
private void recur(int[] candidates, int target, int pos){
if(pos == candidates.length){
return;
}
if(target == 0){
res.add(new LinkedList<Integer>(path));
return;
}
// 跳过当前元素
recur(candidates, target, pos + 1);
// 选择当前元素 因为下次还在在当前位置开始,所以可以选择0-n次
if(target - candidates[pos] >= 0){
path.add(candidates[pos]);
recur(candidates, target - candidates[pos], pos);
path.remove(path.size() - 1);
}
}
}
class Solution {
List<List<Integer>> res;
List<Integer> path;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
res = new LinkedList<List<Integer>>();
path = new LinkedList<Integer>();
backstrack(candidates, 0, target);
return res;
}
private void backstrack(int[] candidates, int pos, int target){
if(target < 0 || pos >= candidates.length){
return;
}
if(target == 0){
res.add(new LinkedList<Integer>(path));
return;
}
// 选择当前元素 因为下次还在在当前位置开始,所以可以选择0-n次
path.add(candidates[pos]);
backstrack(candidates, pos, target - candidates[pos]);
// 跳过当前元素
path.remove(path.size() - 1);
backstrack(candidates, pos + 1, target);
}
}
- 时间复杂度 O(S) :其中 S 为所有可行解的长度之和
- 空间复杂度 O(target)