持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情🚀🚀
前言
今天居然只有一道题,还是出乎意料的,所以这个题还是认认真真写了一下的~
77. 组合 - 力扣(LeetCode)
自己的想法
看到了示例给的用例,很容易让人想到用暴力去解决这类问题,正当自己上手准备写的时候发现,诶,这里的k和n都是是可以变的,那岂不是每次遍历次数都不一样?然后就一团浆糊了。。。
实现代码
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
};
这里的思想还是用暴力搜索,只不过使我们变得更好地去实现暴力搜索的过程。
思路分析
拿卡哥的图举例:
- 最开始的第一层for循环里对应着就是图中的第一层,在图中第一层是由1,2,3,4四个数组成的,每一个数字都有其分支。
- 需要记录每一分支过程的结果,用
path记录。每到一层就记录。 - 当
path当前分支记录的数量符合题意时,就可以将path加入到结果数组中。
注意
- 这里将path数组加入到结果数组res中需要
深拷贝的形式 - 被忘了回溯法的精华,每次递归出来后,都要path.pop();
- 注意这里是
++startIndex
优化
回溯法虽然是暴力搜索,但也有时候可以有点剪枝优化一下的。 来举一个例子,n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了。
可以再参考下图:
如果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
};