216. 组合总和 III
216. 组合总和 III
题目来源
题目分析
题目要求找出所有相加之和为
n
的k
个数的组合。这些组合只能包含1
到9
的正整数,并且每个组合中的数字不能重复出现。解集也不能包含重复的组合。
题目难度
- 难度:中等
题目标签
- 标签:数组, 回溯
题目限制
1 <= k <= 9
1 <= n <= 60
解题思路
这个问题可以使用回溯算法来解决。我们需要从 1
到 9
中选择 k
个数,使得这 k
个数的和为 n
。每个数只能选择一次,并且组合中不能包含重复的数字。
其实这题是77.组合多加了一个返回的组合的和要等于n
的条件罢了,可以把77.组合的回溯代码改一改,加个判断和是否是n
的限定条件就可以了,但是这题因为多了个限制条件,可以根据这个条件使用更多的方法剪枝。
核心算法步骤
- 回溯算法:
- 从
1
-9
选择数字,从大到小枚举(从小到大也行,但是剩余元素的和就不好写),由于不能重复,人为规定,每次选择的数字要是递减的,每次选择一个数字后,将 选择的数字j
加入路径path
末尾,同时将目标和t
减去该数字,然后向下递归枚举。 - 边界条件:当
d
为0
时,无需判断当前组合的和是否为n
,因为使用目标和t
条件的剪枝之后,能选完的组合和肯定是n
,将该组合加入到结果集中。 - 通过剪枝可以提高效率,避免不必要的递归。
- 如果剩余元素小于要选择的的个数
d
,剪枝 t
为剩下要凑的和,如果路径path
中的和已经大于t
,已经凑多了,则剪枝,或者剩下的最大d
个数的和(即i
到i-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
。 - 当剩余的
d
为0
时,无需检查当前路径上的和是否为目标值n
,将该路径添加到结果集中。
- 我们从
-
剪枝逻辑:
- 剪枝的条件基于剩余的目标和
t
和k
。如果剩余的目标和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
,可以减少递归的次数,使算法更加高效。
其他实现
除了回溯法,还可以通过动态规划或广度优先搜索来求解组合问题,但这些方法可能不如回溯法直观和高效。
总结
这道题目通过回溯算法帮助我们理解了如何从一组数字中生成满足条件的组合。通过剪枝,我们能够有效减少不必要的计算,提升算法的性能。这种思路在解决类似的组合数学问题时非常有效。
标签:216,剪枝,题目,组合,int,回溯,path,III,LeetCode From: https://blog.csdn.net/Lentr0py/article/details/141111963