题目

力扣题目链接

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。

示例 1:

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

示例 2:

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

解题思路

本题就是在 [1,2,3,4,5,6,7,8,9] 这个集合中找到和为 n 的 k 个数的组合。需要注意的是,组合是不要求子集先后顺序的。

也就是说,相较于 77.组合 这道题,本题多了一个限制,本题是要找到和为 n 的 k 个数的组合,且整个集合已经是固定的了 [1, … ,9] 。

想到这一点了,做过 77.组合 这道题之后,本题是简单一些了。

本题 k 相当于树的深度,9 就是树的宽度(因为整个集合就是9个数)。

例如 k = 2,n = 4 的话,就是在集合 [1,2,3,4,5,6,7,8,9] 中求 k(个数) = 2, n(和) = 4的组合。

选取过程如图:
image.png
图中,可以看出,只有最后取到集合(1,3)和为4 符合条件。

按照 77.组合 这道题的解题套路,我们不难写出下面的代码:

class Solution {
    int n, k;

    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();

    // sum 为已经收集的元素的总和,也就是path里元素的总和
    int sum = 0;

    public List<List<Integer>> combinationSum3(int k, int n) {
        this.n = n;
        this.k = k;

        backtracking(1);

        return result;
    }

    void backtracking(int startIndex) {
        // 终止条件
        if (path.size() == k) {
            // 如果 sum == n
            if (sum == n) {
                // 加入到结果集中
                result.add(new ArrayList<>(path));
            }
            return;
        }

        for (int i = startIndex; i <= 9; i++) {
            path.add(i);
            sum += i;

            backtracking(i + 1);

            path.removeLast();
            sum -= i;
        }
    }
}

还要强调一下,回溯法中递归函数参数很难一次性确定下来,一般先写逻辑,需要啥参数了,填什么参数。

老规矩,还是要聊一下剪枝

剪枝

这道题目,剪枝操作其实是很容易想到了,想必大家看上面的树形图的时候已经想到了。为了方便阅读,我把上面那张图放到这里:
image.png

已选元素总和如果已经大于n(图中数值为4)了,那么往后遍历就没有意义了,直接剪掉。

那么剪枝的地方一定是在递归终止的地方剪,代码如下:

// 剪枝
if (sum > n) {
    return;
}

77.组合 的剪枝一样,for 循环的范围也可以剪枝,i <= 9 - (k - path.size()) + 1就可以了。

for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) { // 剪枝

答案

Java

class Solution {
    // 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
    int n, k;

    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();

    // sum 为已经收集的元素的总和,也就是path里元素的总和
    int sum = 0;

    public List<List<Integer>> combinationSum3(int k, int n) {
        this.n = n;
        this.k = k;

        backtracking(1);

        return result;
    }

    void backtracking(int startIndex) {
        // 剪枝
        if (sum > n) {
            return;
        }

        // 终止条件
        if (path.size() == k) {
            if (sum == n) {
                // 加入到结果集中
                result.add(new ArrayList<>(path));
            }
            return;
        }

        for (int i = startIndex; i <= 9; i++) {
            path.add(i);
            sum += i;

            backtracking(i + 1);

            path.removeLast();
            sum -= i;
        }
    }
}

REF

https://programmercarl.com/0216.组合总和III.html
https://leetcode-cn.com/problems/combination-sum-iii/