Question
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
Solution
Draw out solution tree. Given [4, 2]
[]
/ / \ \
1 2 3 4
/ | \ / \ |
2 3 4 3 4 4
According to this tree, we can write out solution.
public class Solution {
public List<List<Integer>> combine(int n, int k) {
if (k > n)
k = n;
List<List<Integer>> result = new ArrayList<List<Integer>>();
dfs(n, k, 1, new ArrayList<Integer>(), result);
return result;
} private void dfs(int n, int k, int start, List<Integer> list, List<List<Integer>> result) {
if (list.size() == k) {
result.add(new ArrayList<Integer>(list));
return;
} for (int i = start; i <= n; i++) {
list.add(i);
dfs(n, k, i + 1, list, result);
list.remove(list.size() - 1);
}
}
}
Conclusion -- start pointer or visited[]
总结一下Backtracking这一类常见问题。
Backtracking一般都是应对穷举来找出所有答案的问题。对于这类问题,我们常常可以画出解空间树。然后遍历解空间树就可以得到答案。
遍历可以用 BFS 也可以DFS。一般来说,用迭代实现的DFS,不用自己维护栈所以是比较方便的做法。如果用BFS,则需要纪录一系列状态值。
在迭代的时候,我们要注意迭代结束之后要回到未迭代时的状态。
1. 如果有list操作,要把list的最后一个element移出
2. 如果有visited[],重新设置该element的状态为未访问过。
对比这些题目 Permutation, PhoneNumber,我们发现有的时候用到了start指针来代替visited[]数组表明该结点都无访问过。
那什么时候用start指针,什么时候用visited[]呢?