77,组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
回溯法解决的问题可以抽象为树形结构,因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,构成树的深度。
递归就要有终止条件,所以必然是一棵有限的树(N叉树)。
/**
* @param {number} n
* @param {number} k
* @return {number[][]}
*/
var combine = function(n, k) {
let result = []; //收集结果集
let path = []; //存放符合条件的结果
var clone = function (params) {
let arr = [];
for(let i = 0;i<params.length;i++){
arr.push(params[i]);
}
return arr;
}
//n相当于树的宽度,k相当于树的深度
var backtracking = function (n,k,startIndex) {
if(path.length == k){
result.push(clone(path));
return;
}
for(let i = startIndex;i <= n-(k-path.length())+1;i++){ //剪枝优化
path.push(i);
backtracking(n,k,i+1);
path.pop();
}
};
backtracking(n,k,1);
return result;
};
combine(4,2);
剪枝优化:
举一个例子,n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了。 在第二层for循环,从元素3开始的遍历都没有意义了
如果for循环选择的起始位置之后的元素个数已经不足我们需要的元素的个数了,就没要搜索了。
1.已经选择的元素的个数:path.length;
2.所需元素个数为:k-path.length;
3.剩余元素(n-i)>= k-path.length
4.在集合n中至多要从该起始位置开始遍历, i <= n-(k-path.length)+1,,加1 因为包括起始位置;
例如 n =4,k =3,n -(k-0)+1 ,4-3+1=2,从2开始搜索,组合[2,3,4]是合理的;