int n; int totals; List<List<Integer>> ans = newArrayList<>(); publicvoidbacktrack( int first,ArrayList<Integer> curr){ if(curr.size() == totals){ ans.add(newArrayList<>(curr)); return; }
public List<List<Integer>> combine(int n, int k) { if (n == 0 || k == 0 || k > n) return Collections.emptyList(); List<List<Integer>> res = newArrayList<List<Integer>>(); //个数为 1 的所有可能 for (inti=1; i <= n + 1 - k; i++) res.add(Arrays.asList(i)); //第一层循环,从 2 到 k for (inti=2; i <= k; i++) { List<List<Integer>> tmp = newArrayList<List<Integer>>(); //第二层循环,遍历之前所有的结果 for (List<Integer> list : res) { //第三次循环,对每个结果进行扩展 //从最后一个元素加 1 开始,然后不是到 n ,而是和解法一的优化一样 //(k - (i - 1) 代表当前已经有的个数,最后再加 1 是因为取了 n for (intm= list.get(list.size() - 1) + 1; m <= n - (k - (i - 1)) + 1; m++) { List<Integer> newList = newArrayList<Integer>(list); newList.add(m); tmp.add(newList); } } res = tmp; } return res; }
组合公式
C ( n, k ) = C ( n - 1, k - 1) + C ( n - 1, k ) 从 n 个数字选 k 个,我们把所有结果分为两种,包含第 n 个数和不包含第 n 个数。这样的话,就可以把问题转换成:
从 n - 1 里边选 k - 1 个,然后每个结果加上 n
从 n - 1 个里边直接选 k 个。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public List<List<Integer>> combine(int n, int k) { if (k == n || k == 0) { List<Integer> row = newLinkedList<>(); for (inti=1; i <= k; ++i) { row.add(i); } returnnewLinkedList<>(Arrays.asList(row)); } // n - 1 里边选 k - 1 个 List<List<Integer>> result = combine(n - 1, k - 1); //每个结果加上 n result.forEach(e -> e.add(n)); //把 n - 1 个选 k 个的结果也加入 result.addAll(combine(n - 1, k)); return result; }