标签:
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], ]
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.
1 public class Solution { 2 public List<List<Integer>> combine(int n, int k) { 3 if (k > n) 4 k = n; 5 List<List<Integer>> result = new ArrayList<List<Integer>>(); 6 dfs(n, k, 1, new ArrayList<Integer>(), result); 7 return result; 8 } 9 10 private void dfs(int n, int k, int start, List<Integer> list, List<List<Integer>> result) { 11 if (list.size() == k) { 12 result.add(new ArrayList<Integer>(list)); 13 return; 14 } 15 16 for (int i = start; i <= n; i++) { 17 list.add(i); 18 dfs(n, k, i + 1, list, result); 19 list.remove(list.size() - 1); 20 } 21 } 22 }
总结一下Backtracking这一类常见问题。
Backtracking一般都是应对穷举来找出所有答案的问题。对于这类问题,我们常常可以画出解空间树。然后遍历解空间树就可以得到答案。
遍历可以用 BFS 也可以DFS。一般来说,用迭代实现的DFS,不用自己维护栈所以是比较方便的做法。如果用BFS,则需要纪录一系列状态值。
在迭代的时候,我们要注意迭代结束之后要回到未迭代时的状态。
1. 如果有list操作,要把list的最后一个element移出
2. 如果有visited[],重新设置该element的状态为未访问过。
对比这些题目 Permutation, PhoneNumber,我们发现有的时候用到了start指针来代替visited[]数组表明该结点都无访问过。
那什么时候用start指针,什么时候用visited[]呢?
??????????????????我是树的分割线??????????????????
关键还在于画出的解空间树。
比如Combination这道题
[]
/ / \ \
1 2 3 4
/ | \ / \ |
2 3 4 3 4 4
我们发现当第一个元素选为2,它再往下走的时候,就不用再从1开始遍历,否则时间空间浪费太对,会time limits exceed。
再看Permutation的解空间树
[]
/ / \ \
1 2 3 4
/ | \ / | \ / | \ / | \
2 3 4 1 3 4 1 2 4 1 2 3
...............
我们发现对于每一层的每一个结点,接下来它要遍历的个数都是3。因此这里我们需要visited[]来判断在一条route上的点是否遍历过。
后记
其实对于树的遍历,我们也不需要visited[],因为树没有环。而对于图的遍历,我们需要visited[]。
标签:
原文地址:http://www.cnblogs.com/ireneyanglan/p/4888799.html