Python
法1:DFS+回溯_选或不选
# 写法1
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res, path = [], []
self.dfs(candidates, 0, res, path, target)
return res
def dfs(self, candidates, start, res, path, residual):
if start <= len(candidates) and residual == 0:
res.append(path.copy())
return
if start >= len(candidates) or residual < 0:
return
path.append(candidates[start])
self.dfs(candidates, start, res, path, residual - candidates[start]) # 选择candidates[start]
path.pop()
self.dfs(candidates, start+1, res, path, residual) # 不选择candidates[start]
# 写法2
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res, tmp = list(), list()
candidates.sort()
self.dfs(candidates, 0, target, res, tmp)
return res
def dfs(self, candidates, start, target, res, tmp):
if target == 0:
res.append(tmp.copy())
return
if start == len(candidates) or target < candidates[start]:
return
self.dfs(candidates, start+1, target, res, tmp) # 不选择当前数字
tmp.append(candidates[start])
self.dfs(candidates, start, target - candidates[start], res, tmp) # 选择当前数字
tmp.pop() # 回溯: 恢复现场
# return # 最后这里其实有个默认return, 可写可不写!
旧解法
法1:回溯
必须掌握基本算法!!!
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
dfs(candidates, 0, target, res, tmp);
return res;
}
public void dfs(int[] candidates, int idx, int target, List<List<Integer>> res, List<Integer> tmp) {
if (idx == candidates.length) {
return;
}
if (target == 0) {
res.add(new ArrayList<>(tmp));
return;
}
dfs(candidates, idx + 1, target, res, tmp); // 不选idx
if (target >= candidates[idx]) { // 选择idx
tmp.add(candidates[idx]);
dfs(candidates, idx, target - candidates[idx], res, tmp);
tmp.remove(tmp.size() - 1);
}
}
}
法2:回溯+剪枝
先排序,再dfs,可以进一步剪枝
参考链接
class Solution {
void backtrack(List<Integer> state, int target, int[] choices, int start, List<List<Integer>> res) {
// 子集和等于 target 时,记录解
if (target == 0) {
res.add(new ArrayList<>(state));
return;
}
// 遍历所有选择
// 剪枝二:从 start 开始遍历,避免生成重复子集
for (int i = start; i < choices.length; i++) {
// 剪枝一:若子集和超过 target ,则直接结束循环
// 这是因为数组已排序,后边元素更大,子集和一定超过 target
if (target - choices[i] < 0) {
break;
}
// 尝试:做出选择,更新 target, start
state.add(choices[i]);
// 进行下一轮选择
backtrack(state, target - choices[i], choices, i, res);
// 回退:撤销选择,恢复到之前的状态
state.remove(state.size() - 1);
}
}
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<Integer> state = new ArrayList<>(); // 状态(子集)
Arrays.sort(candidates); // 对 candidates 进行排序
int start = 0; // 遍历起始点
List<List<Integer>> res = new ArrayList<>(); // 结果列表(子集列表)
backtrack(state, target, candidates, start, res);
return res;
}
}