携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。你可以按 任何顺序 返回答案。
来源:力扣(LeetCode) 链接:leetcode.cn/problems/co…
分析
- 在刷了这么多题目后,应该不会有人用暴力解法了吧,几个数就几重循环,肯定是不符合题意的
- 看到组合,可以想到排列组合公式C(n,k) = C(n-1,k)+C(n-1,k-1),回忆一下:从n个数中选k个数的所有组合以选了第n个数和没选第n个数两类,如果没选第n个数,那么就是从n-1个数中选k个,如果选了第n个数,那么就是从n-1个数中选k-1个,递归公式出来了,自然可以是用递归来解
代码
/**
* @param {number} n
* @param {number} k
* @return {number[][]}
*/
var combine = function(n, k, numsArr = Array(n).fill(1).map((n,i)=> n+i)) {
if(k === 0) return [[]]
if(numsArr.length < k) return []
let first = numsArr[0]
let otherCombos = combine(n,k-1, numsArr.slice(1))
let hasFirstCombos = combine(n-1, k-1, numsArr.slice(1))
let noFirstCombos = []
for(let combo of otherCombos){
noFirstCombos.push([first,...combo])
}
return [...noFirstCombos, ...hasFirstCombos]
};
- 可以回想之前在学组合这部分的时候,在没有公式之前,我们是通过画图的方式来数组合,比如有4个节点分别是[1,2,3,4],选2个数,根节点作为开始,之后会生成4个分支,分别是1,2,3,4,在1这个子分支下,第二个数可能是2,3,4,以此类推,这样分析得到的组合就是一个树的形态,n是树的宽度,k是树的深度,这个方法就是回溯法,是暴力求解的一种
- 在求组合的时候,会发现有重复的结果,在回溯的时候做了多余的操作,这些多余的操作在树上就像是多余的树枝,因此回溯法的优化叫剪枝法,即去掉不需要搜索的子分支
代码
var combine = function(n,k){
const res = []
const backtracking = (current,startNum,k)=>{
if(n-startNum +1 < k) return
if(k === 0) return res.push(current)
for(let i=startNum;i<=n;i++){
const newCurrent = [...current]
newCurrent.push(i)
backtracking(newCurrent, i+1, k-1)
}
}
backtracking([],1,k)
return res
}
总结
- 这是一道比较经典的回溯算法的题
- 人生路上也是这样,记得来时的路,记得路上的坎儿,不断的向前探索,才能最快的找到一条适合自己的路
- 今天也是有收获的一天