LeetCode39:组合总和
原题地址:. - 力扣(LeetCode)
题目描述
给你一个 无重复元素 的整数数组
candidates
和一个目标整数target
,找出candidates
中可以使数字和为目标数target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。对于给定的输入,保证和为
target
的不同组合数少于150
个。示例 1:
输入:candidates =[2,3,6,7], target =7 输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。示例 2:
输入: candidates = [2,3,5],target = 8 输出: [[2,2,2,2],[2,3,3],[3,5]]示例 3:
输入: candidates =[2],target = 1 输出: []提示:
1 <= candidates.length <= 30
2 <= candidates[i] <= 40
candidates
的所有元素 互不相同1 <= target <= 40
解题思路
从给定的候选数字数组
candidates
中找出所有和为target
的组合。这个问题可以通过深度优先搜索(DFS)算法来解决。代码中定义了两个方法:
combinationSum
:主函数,用于初始化结果列表ans
和临时组合列表combine
,并调用dfs
方法开始深度优先搜索。dfs
:深度优先搜索函数,用于遍历候选数字数组并构建所有可能的组合。在
dfs
方法中,我们有两个选择:
- 跳过当前数字:直接递归调用
dfs
方法,索引加一,尝试下一个数字。- 选择当前数字:如果当前目标
target
大于等于候选数字,则将该数字添加到combine
列表中,并递归调用dfs
方法,目标减去该数字,索引不变,尝试构建下一个数字的组合。递归结束后,需要从combine
中移除最后一个数字,以回溯到上一个状态。
源码实现
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
// 初始化结果列表和临时组合列表
List<List<Integer>> ans = new ArrayList<>();
List<Integer> combine = new ArrayList<>();
// 从索引0开始深度优先搜索
dfs(candidates, target, ans, combine, 0);
return ans;
}
public void dfs(int[] candidates, int target, List<List<Integer>> ans, List<Integer> combine, int idx) {
// 递归终止条件:如果索引等于候选数字数组的长度,返回
if (idx == candidates.length) {
return;
}
// 如果目标和为0,说明找到了一个有效的组合,添加到结果列表中
if (target == 0) {
ans.add(new ArrayList<>(combine));
return;
}
// 直接跳过当前数字,尝试下一个数字
dfs(candidates, target, ans, combine, idx + 1);
// 如果当前目标减去当前数字大于等于0,说明可以选择当前数字
if (target - candidates[idx] >= 0) {
// 选择当前数字,添加到临时组合列表中
combine.add(candidates[idx]);
// 递归调用,目标更新为新的和,索引不变
dfs(candidates, target - candidates[idx], ans, combine, idx);
// 回溯,移除临时组合列表中的最后一个数字
combine.remove(combine.size() - 1);
}
}
}
复杂度分析
时间复杂度:最坏情况下,DFS 会尝试所有可能的组合,时间复杂度为 O(2^n),其中 n 是候选数字数组
candidates
的长度。空间复杂度:空间复杂度主要取决于递归栈的深度和临时组合列表的大小。最坏情况下,递归栈的深度和临时组合列表的大小都不超过 n,所以空间复杂度为 O(n)