回溯算法理论基础
链接
文章链接
理论
回溯算法和递归是相辅相成的,回溯算法就是可以抽象成一棵树,递归的深度是树的深度,每一层的遍历就是树每一层的宽度:
回溯算法一般是有模版的,也是分为三步走:
- 确定参数
- 确定终止条件(一般这里就回收结果了)
- 确定单层逻辑(一般为for循环)
77. 组合
链接
文章链接
题目链接
第一想法
这道题是一道非常常见的回溯算法的题,核心思想就是暴力求解,详细解释会在代码中给出:
function combine(n: number, k: number): number[][] {
let res:number[][]=[] //存储结果
const dfs=(arr:number[],num:number)=>{
if(arr.length==k){ //终止条件
res.push(Array.from(arr)) //收取结果
return
}
for(let i=num;i<=n;i++){ //每一层遍历 这里要从num开始 防止有重复的结果
arr.push(i)
dfs(arr,i+1)
arr.pop() //回溯
}
}
dfs([],1)
return res
};
看完文章后的想法
文章的解法和我的解法是一致的,文章中有详细的解法和题解,这里就简单阐述一下:
这里举的例子是n=4,k=2.这张图详细的体现了为什么要有一个参数num,用于从左向右取数,目的在于不取重复数.这里详细讲讲:
所以i的范围为[num,n-k+nums.length+1],假设n=4,k=3,nums.length=1,num=2时,这层的循环为n-k+nums.length+1=4-3+1+1=3 ,所以for循环为[2,3].4就被剪枝了(详细解释在代码随想录中有讲解).代码如下:
function combine(n: number, k: number): number[][] {
let res:number[][]=[]
const dfs=(arr:number[],num:number)=>{
if(arr.length==k){
res.push(Array.from(arr))
return
}
for(let i=num;i<=n-k+arr.length+1;i++){
arr.push(i)
dfs(arr,i+1)
arr.pop() //回溯
}
}
dfs([],1)
return res
};
思考与总结
今天总共分为两部分,第一部分为回溯算法的理论,另一部分是练习一道组合的题进行初步了解回溯算法,之前了解过回溯算法,所以今年对我来说是回顾,但是对于剪枝优化还是看了比较长的时间,总共耗时2h