LeetCode 77.组合 回溯写法详解

77. 组合

题目来源

77. 组合

题目分析

给定两个整数 nk,要求从 1n 中选择 k 个数,并返回所有可能的组合。这实际上是经典的组合数学问题,可以通过回溯算法来解决。

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。

题目难度

  • 难度:中等

题目标签

  • 标签:数组, 回溯

题目限制

  • 1 <= n <= 20
  • 1 <= k <= n

解题思路

要解决这个问题,我们可以使用回溯算法。回溯的基本思想是从 1n 中逐个选择数字,每次选择后进入下一层递归,直到选满 k 个数字为止。为了提高效率,我们可以在递归过程中剪枝,避免无效的递归。

核心算法步骤

  • 无剪枝的回溯

    1. 从当前数字开始逐个选择,并递归选择剩余的数字。
    2. 当选满 k 个数字后,将当前组合添加到结果集中。
  • 剪枝的回溯

    1. 在递归过程中,通过计算剩余数字是否足够选择,决定是否继续递归,避免不必要的计算。
    2. 通过从大到小枚举剩余数字,保证剩下的数字足够填满剩余的选择,减少搜索空间。

代码实现

以下是生成组合的Java代码:

/**
 * 77. 组合
 * @param n 输入的整数n
 * @param k 输入的整数k
 * @return ans 所有可能的组合
 */
public List<List<Integer>> combine(int n, int k) {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    if (n < k) {
        return ans;
    }
    combine(k, n, path, ans);
    return ans;
}

// 回溯生成组合
private void combine(int k, int i, List<Integer> path, List<List<Integer>> ans) {
//无剪枝
//        if(path.size() == k){
//            ans.add(new ArrayList<>(path));
//            return;
//        }
//        for (int j = i; j <= n; j++) {
//            path.add(j);
//            combine(n, k, j + 1, path, ans);
//            path.remove(path.size() - 1);
//        }
    // 剪枝
    int d = k - path.size(); // 还需选择d个数
    if (d == 0) {
        ans.add(new ArrayList<>(path));
        return;
    }
    // 从大到小枚举,剪枝条件为剩余数不足以填满
    for (int j = i; j >= d; j--) {
        path.add(j);
        combine(k, j - 1, path, ans);
        path.remove(path.size() - 1);
    }
}

代码解读

  • 回溯逻辑

    • 递归调用 combine 函数时,我们在当前路径 path 中添加一个新的数字,并继续递归处理下一个数字。
    • 一旦路径长度达到了 k,即已经选择了 k 个数字,则将当前路径添加到结果集中。
  • 剪枝逻辑

    • 剪枝的关键在于通过计算 d,判断剩余数字是否足够填满所需的选择。
    • 如果剩余数字不够,则立即终止当前分支的递归,节省时间。

性能分析

  • 时间复杂度O(k*C(n, k)),即 n 中选 k 个数的组合数乘以搜索树的路径长度k。回溯算法会遍历每一个可能的组合。
  • 空间复杂度O(k),递归调用栈的最大深度为 k

测试用例

你可以使用以下测试用例来验证代码的正确性:

int n = 4, k = 2;
List<List<Integer>> result = combine(n, k);
System.out.println(result);
// 输出: [[2, 1], [3, 1], [3, 2], [4, 1], [4, 2], [4, 3]]

扩展讨论

优化写法

可以通过预先计算组合数的方式,减少递归的深度,使算法更加高效。

其他实现

除了回溯法,还可以通过动态规划或字典序生成法来求解组合问题。

总结

这道题目通过回溯算法帮助我们理解了如何生成所有可能的组合。通过剪枝,可以有效减少搜索空间,提高算法的效率。这种方法在解决组合数学问题时非常常见且有效。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值