1. 题目
2. 题解
组合问题抽象为如下树形结构:
(1)暴力回溯
class Solution {
List<List<Integer>> res = new ArrayList<>(); //结果集合
List<Integer> path = new ArrayList<>(); //存储路径
public List<List<Integer>> combine(int n, int k) {
backTracking(n, k, 1);
return res;
}
public void backTracking(int n, int k, int startIndex) {
if (path.size() == k) { //达成条件,K个数的路径
res.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i <= n; i++) { //单层循环,逐一取出剩下的数
//当前数加入路径
path.add(i);
backTracking(n, k, i + 1); //继续递归寻找下一层符合条件路径
path.remove(path.size() - 1); //符合条件后回溯,取到12后,回溯弹出2,取到13
}
}
}
(2)回溯(剪枝优化)
来举一个例子,n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了。 在第二层for循环,从元素3开始的遍历都没有意义了。
这么说有点抽象,如图所示:
减枝优化:
- i <= n - (k - path.size())
- 或者:n - i + 1 >= k - path.size()
- 为什么要加1,因为起始位置是包括了startIndex的 (n - i + 1 代表包含了startIndex的)
- 当前还有的 >= 所需要的节点
class Solution {
List<List<Integer>> res = new ArrayList<>(); //结果集合
List<Integer> path = new ArrayList<>(); //存储路径
public List<List<Integer>> combine(int n, int k) {
backTracking(n, k, 1);
return res;
}
public void backTracking(int n, int k, int startIndex) {
if (path.size() == k) { //达成条件,K个数的路径
res.add(new ArrayList<>(path));
return;
}
//减枝优化:k - path.size()为当前还需要的,n - (k - path.size()) + 1为
//当前还有的 >= 所需要的节点
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) {
path.add(i);
backTracking(n, k, i + 1);
path.remove(path.size() - 1);
}
}
}