持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
一、题目描述
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:
输入:n = 1, k = 1
输出:[[1]]
提示:
1 <= n <= 201 <= k <= n
题目来源:77. 组合
二、思路分析
这类题放在生活中,就是一道排列组合的数学题,我们要解题,自然会考虑到数字间的组合,一般是从小到大进行配对,穷举直到最大的数字为止。
按照这样的思路,我们可以将其转化为代码,就是循环里面再循环,也就是递归。难点在于,当我们配对成功后,怎么继续往下配对,因为当前配对成功只是答案的其中一个。其实就是需要将配对的那个数字删除(假如配对两个数),这样只剩一个的那个数字就能继续往下配对。
这道题的思路分析,我想自顶向下讲解,先贴 AC 代码,然后讲解其中思路。
首先在本地新建 test.js 文件,用于调试代码:
var combine = function(n, k) {
const res = [], track = [];
const backtrack = (n, k, start, track) => {
console.log('res', res)
console.log('track', track)
if (track.length === k) {
console.log('track.length === k')
res.push(Array.from(track));
return;
}
for (let i = start; i <= n; i++) {
console.log('push', i)
track.push(i);
backtrack(n, k, i + 1, track);
track.pop();
console.log('pop', i)
}
}
backtrack(n, k, 1, track);
return res;
};
combine(4, 2);
运行 node test.js 后,可以看到如下打印信息:
res []
track []
push 1
res []
track [ 1 ]
push 2
res []
track [ 1, 2 ]
track.length === k
pop 2
push 3
res [ [ 1, 2 ] ]
track [ 1, 3 ]
track.length === k
pop 3
push 4
res [ [ 1, 2 ], [ 1, 3 ] ]
track [ 1, 4 ]
track.length === k
pop 4
pop 1
push 2
res [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ] ]
track [ 2 ]
push 3
res [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ] ]
track [ 2, 3 ]
track.length === k
pop 3
push 4
res [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 3 ] ]
track [ 2, 4 ]
track.length === k
pop 4
pop 2
push 3
res [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ] ]
track [ 3 ]
push 4
res [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ] ]
track [ 3, 4 ]
track.length === k
pop 4
pop 3
push 4
res [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ], [ 3, 4 ] ]
track [ 4 ]
pop 4
按照前文说的,从最小的数字开始匹配,不满足条件则往下走,若满足条件则将其存入答案中,同时将最近的那个数字删除,然后接着往下匹配。
由于是递归,需要在递归函数最开始定义 bad case ,才能退出递归,这里的 bad case 就是 track.length === k ,这很好理解,配对的数量达到要求后,自然就能存入答案中了,然后 return 出去。return 之后,会退回到上一个递归函数中,从递归的地方继续往下走,也就是会执行 track.pop() ,这就是我说的 “满足条件则将其存入答案中,同时将最近的那个数字删除”。
三、AC 代码
/**
* @param {number} n
* @param {number} k
* @return {number[][]}
*/
var combine = function(n, k) {
const res = [], track = [];
const backtrack = (n, k, start, track) => {
if (track.length === k) {
res.push(Array.from(track));
return;
}
for (let i = start; i <= n; i++) {
track.push(i);
backtrack(n, k, i + 1, track);
track.pop();
}
}
backtrack(n, k, 1, track);
return res;
};