回溯算法也就是递归函数,指的都是一个函数
回溯算法理论基础
将回溯算法看作一个树,for循环就是从左到右,集合元素数量就是树的宽度;递归就是从上到下
代码模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
刷题
77. 组合
最基础的回溯算法,直接套用模板就行,但要注意有一个剪枝的操作,即for循环中i的终止条件,当剩下的元素数量小于k时,就要进行终止了。
描述
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
代码
let path = []
let res = []
var combine = function(n, k) {
res = []
backtracking(n, k, 1)
return res
};
const backtracking = (n, k, startIndex)=>{
if(path.length === k){
res.push([...path])
return
}
for(let i = startIndex; i <= (n - (k-path.length) + 1); i++){ // 优化的地方
path.push(i)
backtracking(n, k, i+1)
path.pop()
}
}
216.组合总和3
描述
找出所有相加之和为 n **的 k ****个数的组合,且满足下列条件:
- 只使用数字1到9
- 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
分析
-
每个数字只使用一次——通过递归时传递的startIndex进行控制,每次递归startIndex加一
-
列表内不能包含重复的组合——for循环从startIndex开始已经能够实现这个条件
-
需要有一个sum来计算每一次递归的所有元素之和,同时可以通过比较sum与n的大小进行剪枝
代码
let res = []
let path = []
var combinationSum3 = function(k, n) {
res = []
backtracking(k, n, 1, 0)
return res
};
const backtracking = (k, n, startIndex, sum) => {
if(sum > n){ // 剪枝1
return
}
if(sum === n && path.length === k){
res.push([...path])
}
for(let i = startIndex; i<= 10-(k-path.length); i++){ // 剪枝2
path.push(i)
backtracking(k, n, i+1, sum+i)
path.pop()
}
}