组合问题:n个数里按一定规则找出k个数的集合(大多都是这样问)
比如leetcode 组合 这道题
题目要求:给定两个整数 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为例,这道题就是在 1,2,3,4(n)中选取2(k)个数(不能重复选,[1,2]与[2,1]就重复)
先从上图的树结构顺一下思路:
从1开始,得到2,满足条件(得到[1,2]),返回
得到3,满足条件(得到[1,3]),返回
得到4,满足条件(得到[1,4]),返回
从2开始,得到3,满足条件(得到[2,3]),返回
得到4,满足条件(得到[2,4]),返回
从3开始,得到4,满足条件(得到[3,4]),返回
从4开始,得不到任何结果,返回
写出伪代码:
// cur 就是当前值
dfs(cur, n, k) {
// 终止条件:得到k个数记录并返回
if(得到k个数) return;
// 搜索:从cur-n
for (i=cur...n) {
记录当前值;
// 从cur+1继续搜索
dfs(cur + 1, n, k);
// 删除目前存的最后一个数
删除数;
}
}
写出完整的回溯:
class Solution {
// temp 存k个值
List<Integer> temp = new ArrayList<>();
// ans 存符合要求的temp
List<List<Integer>> ans = new ArrayList<>();
public List<List<Integer>> combine(int n, int k) {
// cur 从1开始
dfs(1, n, k);
return ans;
}
public void dfs(int cur, int n, int k) {
if (temp.size() == k) {
ans.add(new ArrayList<>(temp));
return;
}
for (int i = cur; i <= n; i++) {
temp.add(i);
dfs(i + 1, n, k);
temp.remove(temp.size() - 1);
}
}
}
能不能优化一下dfs,提高一下效率呢?
当for循环起始位置之后的个数( n - i + 1)< 需要的个数( k - temp.size() ),就没必要继续搜索
即满足:
n - i + 1 >= k - temp.size()
那么上述dfs就可以优化,如下
public void dfs(int cur, int n, int k) {
if (temp.size() == k) {
ans.add(new ArrayList<>(temp));
return;
}
for (int i = cur; i <= n - (k - temp.size()) + 1; i++) {
temp.add(i);
dfs(i + 1, n, k);
temp.remove(temp.size() - 1);
}
}
除了用上述方法来解,还有其它的思路吗?
方法二:
如下图:
同样我们能写出dfs:
public void dfs(int cur, int n, int k) {
// 剪枝:选择的数 + 剩余的数 < 需要的个数k(与方法一同理)
if (temp.size() + (n - cur + 1) < k) {
return;
}
if (temp.size() == k) {
ans.add(new ArrayList<>(temp));
return;
}
// 选择 cur
temp.add(cur);
dfs(cur + 1, n, k);
temp.remove(temp.size() - 1);
// 不选择 cur
dfs(cur + 1, n, k);
}
这里选与不选都进行同样的操作:cur+1 进入下一层
这两种方法( 参考了 力扣 题解)
如果这道题理解并会做的话,那就对回溯有了一些基本的认识,推荐做一些类似的题目(如下)来加深理解。