题目描述:
题号:39
给你一个 无重复元素 的整数数组 candidates
和一个目标整数 target
,找出 candidates
中可以使数字和为目标数 target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target
的不同组合数少于 150
个。
解题思路:
思路一:回溯
解题的核心思想是使用回溯算法(Backtracking)。
-
-
初始化:
-
创建一个空的切片
temp
来存储当前的组合。 -
定义一个递归函数
dfs
,它接受两个参数:剩余的目标值target
和当前考察的数组索引idx
。
-
-
递归基准情况:
-
如果
idx
等于candidates
的长度,说明已经考察完所有候选数,但此时若target
不为 0,则不满足条件,直接返回。 -
如果
target
等于 0,说明当前组合的数字之和等于目标值,将temp
添加到结果集ans
中,并返回。
-
-
递归选择:
-
跳过当前数:不选择
candidates[idx]
,继续考察下一个数,即调用dfs(target, idx+1)
。 -
选择当前数:如果
target - candidates[idx]
大于等于 0,说明可以选择candidates[idx]
,将其添加到temp
中,并递归调用dfs(target-candidates[idx], idx)
(注意idx
没有变,因为数字可以被无限次使用)。
-
-
回溯:
-
在递归调用之后,无论是否找到了满足条件的组合,都需要将刚刚添加到
temp
中的数移除,以便进行下一次选择或回溯到上一层。
-
-
开始递归:
-
从
target
和索引0
开始调用递归函数dfs
。
-
-
返回结果:
-
递归完成后,返回结果集
ans
。
-
-
时间复杂度:最坏情况是O(2^target)(当candidates
包含1时)
空间复杂度:O(target + |ans|),其中|ans|
表示结果集中组合的数量。
C++
// C++
class Solution {
vector<int> temp;
vector<vector<int>> answer;
void backTrace(vector<int>& candidates, int target, int sum, int index) {
if(sum == target) {
answer.push_back(temp);
return;
}
if(sum > target) {
return;
}
for(int i = index; i < candidates.size(); i++) {
sum += candidates[i];
temp.push_back(candidates[i]);
backTrace(candidates, target, sum, i);
temp.pop_back();
sum -= candidates[i];
}
}
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
if(candidates.size() == 0) {
return {};
}
backTrace(candidates, target, 0, 0);
return answer;
}
};
go
// go
func combinationSum(candidates []int, target int) (ans [][]int) {
temp := []int{}
var dfs func(target, idx int)
dfs = func(target, idx int) {
if idx == len(candidates) {
return
}
if target == 0 {
ans = append(ans, append([]int(nil), temp...))
return
}
dfs(target, idx+1)
// 选择当前数
if target-candidates[idx] >= 0 {
temp = append(temp, candidates[idx])
dfs(target-candidates[idx], idx)
temp = temp[:len(temp)-1]
}
}
dfs(target, 0)
return
}