✅✅代码随想录算法训练营Day24 || 77. 组合

258 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情🚀🚀

前言

今天居然只有一道题,还是出乎意料的,所以这个题还是认认真真写了一下的~

77. 组合 - 力扣(LeetCode)

image.png

自己的想法

看到了示例给的用例,很容易让人想到用暴力去解决这类问题,正当自己上手准备写的时候发现,诶,这里的kn都是是可以变的,那岂不是每次遍历次数都不一样?然后就一团浆糊了。。。

实现代码

var combine = function(n, k) {
    let path = [];
    let res = [];
    const dfs = (n,path,startIndex) => {
            if(path.length == k){
                res.push([...path])
                // console.log(res)
                return 
            }
            for(let i = startIndex; i <= n;i++){
                path.push(i)
                dfs(n,path,++startIndex)
                path.pop();
            }
    }
    dfs(n,path,1);
    return res
};

这里的思想还是用暴力搜索,只不过使我们变得更好地去实现暴力搜索的过程。

思路分析

拿卡哥的图举例: image.png

  • 最开始的第一层for循环里对应着就是图中的第一层,在图中第一层是由1,2,3,4四个数组成的,每一个数字都有其分支。
  • 需要记录每一分支过程的结果,用path记录。每到一层就记录。
  • path当前分支记录的数量符合题意时,就可以将path加入到结果数组中。

注意

  1. 这里将path数组加入到结果数组res中需要深拷贝的形式
  2. 被忘了回溯法的精华,每次递归出来后,都要path.pop();
  3. 注意这里是++startIndex

优化

回溯法虽然是暴力搜索,但也有时候可以有点剪枝优化一下的。 来举一个例子,n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了。

可以再参考下图:

image.png

如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了

剪枝后的代码

var combine = function(n, k) {
    let path = [];
    let res = [];
    const dfs = (n,path,startIndex) => {
            if(path.length == k){
                res.push([...path])
                return 
            }
            // k-path.length 代表该层后面还剩几个数字可以遍历
            for(let i = startIndex; i <= n - (k - path.length) + 1;i++){
                path.push(i)
                dfs(n,path,++startIndex)
                path.pop();
            }
    }
    dfs(n,path,1);
    return res
};