难度中等853
给定一个无重复元素的数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的数字可以无限制重复被选取。
说明:
- 所有数字(包括
target
)都是正整数。 - 解集不能包含重复的组合。
示例 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]
]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate
中的每个元素都是独一无二的。1 <= target <= 500
通过次数134,377提交次数192,339
用dfs
### 回朔法的思想: 回朔法的重要思想在于: 通过枚举法,对所有可能性进行遍历。 但是枚举的顺序是 一条路走到黑,发现黑之后,退一步,再向前尝试没走过的路。直到所有路都试过。因此回朔法可以简单的理解为: 走不通就退一步的方枚举法就叫回朔法。而这里回退点也叫做回朔点。
### 回朔关键点 通过分析发现,回朔法实现的三大技术关键点分别是:
- 一条路走到黑
- 回退一步
- 另寻他路
### 关键点的实现 那么如何才能用代码实现上述三个关键点呢?
- for 循环
- 递归
#### 解释如下
-
for循环的作用在于另寻他路: 你可以用for循环可以实现一个路径选择器的功能,该路径选择器可以逐个选择当前节点下的所有可能往下走下去的分支路径。 例如: 现在你走到了节点a,a就像个十字路口,你从上面来到达了a,可以继续向下走。若此时向下走的路有i条,那么你肯定要逐个的把这i条都试一遍才行。而for的作用就是可以让你逐个把所有向下的i个路径既不重复,也不缺失的都试一遍
-
递归可以实现一条路走到黑和回退一步: 一条路走到黑: 递归意味着继续向着for给出的路径向下走一步。 如果我们把递归放在for循环内部,那么for每一次的循环,都在给出一个路径之后,进入递归,也就继续向下走了。直到递归出口(走无可走)为止。 那么这就是一条路走到黑的实现方法。 递归从递归出口出来之后,就会实现回退一步。
因此for循环和递归配合可以实现回朔: 当递归从递归出口出来之后。上一层的for循环就会继续执行了。而for循环的继续执行就会给出当前节点下的下一条可行路径。而后递归调用,就顺着这条从未走过的路径又向下走一步。这就是回朔
说了这么多,回朔法的通常模板是什么呢? 递归和for又是如何配合的呢?
#### 回朔代码模板
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> tmp;
dfs(res, candidates, tmp, target, 0, 0);
return res;
}
void dfs(vector<vector<int>> &res, vector<int>& candidates, vector<int>& tmp, int target, int sum, int begin){
int i;
if(sum == target){
res.push_back(tmp);
return;
}
for(i = begin; i < candidates.size(); i++){
if(sum + candidates[i] <= target){
tmp.push_back(candidates[i]);
dfs(res, candidates, tmp, target, sum + candidates[i], i);
tmp.pop_back();
}
}
}
};