LeetCode39-组合总和
一、题目描述
二、题目分析
2.1 回溯树
组合问题是典型的需要使用回溯算法求解的问题,在处理回溯算法时,可以先思考该问题的回溯树的结构,并尝试绘制问题的回溯树,绘制回溯树的时候,要注意到题目中的一个特殊要求:
同一个数字可以无限制重复选取
,下面是回溯树的图示(以[2,3,6,7]
为例):
2.2 递归终止条件
根据题目很容易想到至少有两个终止条件:
(1)当前遍历路径上的和等于target的时候,那么进行结果收集,并且不在进行下一层的递归
(2)当前遍历路径上的和大于target的时候,那么这条路径可以被直接剪枝,不在进行下一层的递归
三、基本代码实现
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
solve(candidates, target, res, new ArrayList<>(), 0, 0);
return res;
}
// curRes存放当前路径中的结点
// curSum存放当前路径结点的和
// res用来收集所有满足条件的序列
public void solve(int[] candidates, int target, List<List<Integer>> res,List<Integer> curRes, int startIndex, int curSum) {
if (curSum == target) {
res.add(new ArrayList<>(curRes));
return;
}
if (curSum > target) {
return;
}
for (int i = startIndex; i < candidates.length; i += 1) {
curSum += candidates[i];
curRes.add(candidates[i]);
solve(candidates, target, res, curRes, i, curSum);
curSum -= candidates[i];
curRes.remove(curRes.size() - 1);
}
}
}
四、进一步优化
上述给出的两个递归终止条件只能算是回溯算法的基本限界条件,用来终止递归,但是我们并没有给出回溯算法的剪枝条件,curSum > target只能剪掉同一路径下的分枝,不能剪掉同一层上的分支,其实在回溯的过程中,
当我们需要扩展同一层的结点的时候(即for循环的时候),如果同一层的结点时升序扩展的(只需要将原数组升序排序即可),那么我们也可以进行剪枝
,剪枝的条件依然是curSum > target
,优化后的代码如下:
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(candidates);
solve(candidates, target, res, new ArrayList<>(), 0, 0);
return res;
}
/**
* 通过回溯算法求解组合总和问题
*
* @param candidates
* @param target
* @param res 存放所有满足条件的结果
* @param curRes 存放当前搜索路径中满足条件的结果
* @param startIndex 每一层开始的索引
*/
public void solve(int[] candidates,
int target, List<List<Integer>> res,
List<Integer> curRes, int startIndex, int curSum) {
if (curSum == target) {
res.add(new ArrayList<>(curRes));
return;
}
if (curSum > target) {
// 这里只能剪掉同一路径下的分支
return;
}
for (int i = startIndex; i < candidates.length; i += 1) {
curSum += candidates[i];
if (curSum > target) {
// 剪掉同一层的分支,即for循环不再扩展
break;
}
curRes.add(candidates[i]);
solve(candidates, target, res, curRes, i, curSum);
curSum -= candidates[i];
curRes.remove(curRes.size() - 1);
}
}
}