解题框架
步骤
- 路径:也就是已经做出的选择
- 选择列表:也就是你当前可以做的选择
- 结束条件:也就是到达决策树底层,无法再做选择的条件
框架代码
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
39. 组合总和
题目
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
链接:https://leetcode-cn.com/problems/combination-sum
解题思路
- 回溯框架
- 由于可以使用重复元素,每一次搜索的时候设置 下一轮搜索的起点
start
- 从每一层的第 2 个结点开始,都不能再搜索产生同一层结点已经使用过的
candidate
里的元素
代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<Integer> track = new ArrayList<>();
int sum = 0;
backTrack(candidates, target, track, sum, 0);
return res;
}
/**
* @param candidates 候选数组
* @param start 搜索起点
* @param target 目标值
* @param track 从根结点到叶子结点的路径,是一个栈
* @param sum 已求和
*/
void backTrack(int[] candidates, int target, List<Integer> track, int sum, int start) {
// 满足条件,返回
if(sum == target) {
res.add(new ArrayList(track));
return;
}
for(int i = start;i < candidates.length;i++) {
// 大于target直接返回
if(sum + candidates[i] > target)
continue;
track.add(candidates[i]);
// 注意:由于每一个元素可以重复使用,下一轮搜索的起点依然是 i
backTrack(candidates, target, track, sum + candidates[i], i);
track.remove(track.size() - 1);
}
}
}
40. 组合总和 II
题目
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
- 所有数字(包括目标数)都是正整数。
- 解集不能包含重复的组合。
解题思路
回溯框架
-
存在重复元素,为了去重,先对数组排序,遇到重复元素就跳过循环
-
剪枝条件
- target - candidates[i] < 0,由于数组递增,所以直接跳出循环
- 对重复的情况进行剪枝,当candidates[i] == candidates[i-1]时跳过,防止数组越界,i > start
代码
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
Deque<Integer> track = new ArrayDeque<>();
Arrays.sort(candidates);
backTrack(candidates, res, track, target, 0);
return res;
}
/**
* @param candidates 候选数组
* @param res 结果集
* @param target 剩余值
* @param track 从根结点到叶子结点的路径,是一个栈
* @param sum 已求和
*/
void backTrack(int[] candidates, List<List<Integer>> res, Deque<Integer> track, int target, int start) {
if(target == 0) {
res.add(new ArrayList(track));
return;
}
for(int i = start;i < candidates.length;i++) {
if(target - candidates[i] < 0)
break;
// 同一层相同数值的结点,从第 2 个开始,候选数更少,结果一定发