0
点赞
收藏
分享

微信扫一扫

LC77-组合


​​77. 组合​​

难度中等471给定两个整数 nk,返回 1 … n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

回溯法: 是在一棵树上深度优先遍历(欣慰找到所有的解,所以要遍历)

根据搜索起点画出二叉树

深度优先遍历,因此首先画出树形结构,​​n = 4, k = 2​​,我们可以发现如下递归结构:

  • 如果组合里有 1 ,那么需要在 [2, 3, 4] 里再找 11 个数;
  • 如果组合里有 2 ,那么需要在 [3, 4] 里再找 11数。注意:这里不能再考虑 11,因为包含 11 的组合,在第 1 种情况中已经包含。
  • LC77-组合_搜索

说明:

  • 叶子节点的信息体现在从根节点到叶子结点的路径上,因此需要一个表示路径的变量path…他是一个列表,特别的,path是一个栈
  • 每一个结点递归地作同样的事情,区别在于搜索起点,因此需要一个变量​​start​​​,表示在区间​​[begin, n]​​ 里选出若干个数的组合;

class Solution {

public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if (k <= 0 || n < k) {
return res;
}
// 一个表示路径的变量 path
Deque<Integer> path = new ArrayDeque<>();
dfs(n, k, 1, path, res);
return res;
}

private void dfs(int n, int k, int begin, Deque<Integer> path, List<List<Integer>> res) {
// 递归终止条件是:path 的长度等于 k
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}

// 遍历可能的搜索起点
for (int i = begin; i <= n; i++) {
// 向路径变量里添加一个数
path.addLast(i);
// 下一轮搜索,设置的搜索起点要加 1,因为组合数理不允许出现重复的元素
dfs(n, k, i + 1, path, res);
// 重点理解这里:深度优先遍历有回头的过程,因此递归之前做了什么,递归之后需要做相同操作的逆向操作
path.removeLast();
}
}
}

优化:分析搜索起点的上界进行剪枝

// 从当前搜索起点 begin 遍历到 n
for (int i = begin; i <= n; i++) {
path.addLast(i);
dfs(n, k, i + 1, path, res);
path.removeLast();
}

事实上如果​​n = 7, k = 4​​,从5开始搜索就没有什么意义,这是因为:即使把 55 选上,后面的数只有 66 和 77,一共就 33 个候选数,凑不出 44 个数的组合。因此,搜索起点有上界,这个上界是多少,可以举几个

搜索起点的上界 = n - (k - path.size()) + 1

所以,我们的剪枝过程就是:把 ​​i <= n​​​ 改成 ​​i <= n - (k - path.size()) + 1​​ :

class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if (k <= 0 || n < k) {
return res;
}
//一个表示路径的变量 path
Deque<Integer> path = new ArrayDeque<>();
dfs(n, k, 1, path, res);
return res;
}

private void dfs(int n, int k, int index, Deque<Integer> path, List<List<Integer>> res) {
//退出递归
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}

// 只有这里 i <= n - (k - path.size()) + 1 与参考代码 1 不同
for (int i = index; i <= n - (k - path.size()) + 1; i++) {
// // 向路径变量里添加一个数
path.addLast(i);

// 下一轮搜索,设置的搜索起点要加 1,因为组合数理不允许出现重复的元素
dfs(n, k, i + 1, path, res);
//回溯
// 深度优先遍历有回头的过程,因此递归之前做了什么,递归之后需要做相同操作的逆向操作
path.removeLast();
}
}
}


举报

相关推荐

0 条评论