代码随想录算法训练营第二十四天 | 回溯算法理论基础、77. 组合

190 阅读1分钟

回溯算法理论基础

链接

文章链接

理论

回溯算法和递归是相辅相成的,回溯算法就是可以抽象成一棵树,递归的深度是树的深度,每一层的遍历就是树每一层的宽度:

image.png 回溯算法一般是有模版的,也是分为三步走:

  1. 确定参数
  2. 确定终止条件(一般这里就回收结果了)
  3. 确定单层逻辑(一般为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
 };

看完文章后的想法

文章的解法和我的解法是一致的,文章中有详细的解法和题解,这里就简单阐述一下:

image.png 这里举的例子是n=4,k=2.这张图详细的体现了为什么要有一个参数num,用于从左向右取数,目的在于不取重复数.这里详细讲讲:

image.png 所以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