LeetCode 216. 组合总和 III 回溯写法详解

216. 组合总和 III

题目来源

216. 组合总和 III

题目分析

题目要求找出所有相加之和为 nk 个数的组合。这些组合只能包含 19 的正整数,并且每个组合中的数字不能重复出现。解集也不能包含重复的组合。

题目难度

  • 难度:中等

题目标签

  • 标签:数组, 回溯

题目限制

  • 1 <= k <= 9
  • 1 <= n <= 60

解题思路

这个问题可以使用回溯算法来解决。我们需要从 19 中选择 k 个数,使得这 k 个数的和为 n。每个数只能选择一次,并且组合中不能包含重复的数字。
其实这题是77.组合多加了一个返回的组合的和要等于n的条件罢了,可以把77.组合的回溯代码改一改,加个判断和是否是n的限定条件就可以了,但是这题因为多了个限制条件,可以根据这个条件使用更多的方法剪枝。

核心算法步骤

  • 回溯算法
    1. 1 - 9 选择数字,从大到小枚举(从小到大也行,但是剩余元素的和就不好写),由于不能重复,人为规定,每次选择的数字要是递减的,每次选择一个数字后,将 选择的数字j加入路径path 末尾,同时将目标和 t 减去该数字,然后向下递归枚举。
    2. 边界条件:当 d0 时,无需判断当前组合的和是否为 n,因为使用目标和t条件的剪枝之后,能选完的组合和肯定是n,将该组合加入到结果集中。
    3. 通过剪枝可以提高效率,避免不必要的递归。
      1. 如果剩余元素小于要选择的的个数d,剪枝
      2. t为剩下要凑的和,如果路径path中的和已经大于t,已经凑多了,则剪枝,或者剩下的最大d个数的和(即ii-d+1等差数列求和)小于t,不可能凑齐了则剪枝。

代码实现

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

/**
 * 216. 组合总和 III
 * @param k 输入的整数k
 * @param n 输入的整数n
 * @return ans 所有可能的组合
 */
public List<List<Integer>> combinationSum3(int k, int n) {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> path = new ArrayList<>();//组合的可能路径
    if (k > n) {
        return ans;
    }
    combinationSum3(k, 9, n, path, ans);
    return ans;
}

private void combinationSum3(int k, int i, int t, List<Integer> path, List<List<Integer>> ans) {
    int d = k - path.size(); // 还需选择d个数
    // 剪枝条件,如果剩余数t小于0或者大于剩余d个数的最大和,则剪枝
    if (t < 0 || t > ((2 * i - d + 1) * d / 2)) {
        return;
    }
    if (d == 0) {
        ans.add(new ArrayList<>(path));
        return;
    }
    for (int j = i; j >= d; j--) {
        path.add(j);
        combinationSum3(k, j - 1, t - j, path, ans);
        path.remove(path.size() - 1);
    }
}

代码解读

  • 回溯逻辑

    • 我们从 9 开始向下枚举数字,每次选择一个数字后,递归处理剩下的 j-1 个数字,并更新剩余的目标和 t
    • 当剩余的 d0 时,无需检查当前路径上的和是否为目标值 n,将该路径添加到结果集中。
  • 剪枝逻辑

    • 剪枝的条件基于剩余的目标和 tk。如果剩余的目标和 t 小于 0,或者剩余的 d 个最大数的和小于 t,则当前路径不可能满足条件,直接返回。
    • 通过计算等差数列和,可以快速判断是否继续递归。

性能分析

  • 时间复杂度O(k*C(9, k)),回溯算法会遍历所有可能的组合。
  • 空间复杂度O(k),递归调用栈的最大深度为 k

测试用例

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

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

int k = 3, n = 9;
List<List<Integer>> result2 = combinationSum3(k, n);
System.out.println(result2);
// 输出: [[1, 2, 6], [1, 3, 5], [2, 3, 4]]

扩展讨论

优化写法

通过预先判断组合是否有可能达到目标值 n,可以减少递归的次数,使算法更加高效。

其他实现

除了回溯法,还可以通过动态规划或广度优先搜索来求解组合问题,但这些方法可能不如回溯法直观和高效。

总结

这道题目通过回溯算法帮助我们理解了如何从一组数字中生成满足条件的组合。通过剪枝,我们能够有效减少不必要的计算,提升算法的性能。这种思路在解决类似的组合数学问题时非常有效。


  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值