39. 组合总和
查看题目中等
数组
中频
解法一:回溯
时间复杂度:O(S) | 空间复杂度:O(target) | 推荐使用
动画演示
准备就绪 - 输入候选数组和目标值,然后点击开始
候选数组:
当前组合:
空
和: 0 递归栈:
栈空
找到的组合 (0 个):
暂无结果
步骤: 0 | 状态: initial | 当前索引: 0 | 候选元素: 无
代码实现
import java.util.ArrayList;
import java.util.List;
class Solution {
/**
* 找出所有可以使数字和为目标值的组合
*
* @param candidates 候选数字数组
* @param target 目标值
* @return 所有可能的组合
*/
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> combine = new ArrayList<Integer>();
dfs(candidates, target, ans, combine, 0);
return ans;
}
/**
* 深度优先搜索函数
*
* @param candidates 候选数字数组
* @param target 当前目标值
* @param ans 结果集合
* @param combine 当前组合
* @param idx 当前索引
*/
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<Integer>(combine)); // 深拷贝当前组合
return;
}
// 跳过当前数,递归下一个索引
dfs(candidates, target, ans, combine, idx + 1);
// 选择当前数(如果不大于目标值)
if (target - candidates[idx] >= 0) {
combine.add(candidates[idx]);
// 递归搜索,索引不变(可以重复使用当前数)
dfs(candidates, target - candidates[idx], ans, combine, idx);
// 回溯,移除最后添加的数
combine.remove(combine.size() - 1);
}
}
}时间复杂度:O(S)
空间复杂度:O(target)
## 算法思路
### 问题分析
组合总和问题要求找出所有可以使数字和为目标值的组合,其中每个数字可以无限制重复使用。这是一个典型的回溯问题,因为我们需要尝试所有可能的组合,并在找到有效组合时记录下来。
### 算法选择
选择回溯法(深度优先搜索)的原因:
1. 问题需要遍历所有可能的组合,回溯法天然适合这种枚举问题
2. 可以通过剪枝优化,避免无效的搜索路径
3. 实现简单直观,代码结构清晰
### 核心思路
1. **递归函数设计**:
- 参数包括候选数组、目标值、结果集合、当前组合、当前索引
- 当前索引用于避免重复计算(如 [2,3] 和 [3,2] 视为同一组合)
2. **终止条件**:
- 当当前索引超出候选数组范围时,返回
- 当目标值为 0 时,找到有效组合,将其加入结果集并返回
3. **搜索策略**:
- **选择当前数**:如果当前数不大于目标值,将其加入当前组合,递归搜索剩余目标值
- **跳过当前数**:不选择当前数,直接递归搜索下一个数
4. **回溯操作**:
- 在选择当前数后,递归返回时需要将其从当前组合中移除,以便尝试其他可能的组合
### 算法优势
- 时间复杂度较低,避免了重复计算
- 空间复杂度较小,仅使用递归栈和必要的存储
- 实现简单,逻辑清晰
- 可以轻松扩展到其他类似的组合问题
### 示例说明
例如,对于输入 candidates = [2,3,6,7], target = 7:
1. 从索引 0 开始,选择 2,剩余目标值 5
2. 继续选择 2,剩余目标值 3
3. 继续选择 2,剩余目标值 1
4. 无法再选择 2,跳过,尝试选择 3,剩余目标值 -2(无效)
5. 回溯到上一层,尝试跳过当前 2,选择 3
6. 选择 3,剩余目标值 1
7. 无法选择 3,跳过,尝试选择 6(无效)
8. 回溯到最上层,跳过 2,选择 3
9. 选择 3,剩余目标值 4
10. 继续选择 3,剩余目标值 1
11. 回溯,跳过 3,选择 6(无效)
12. 回溯,跳过 3,选择 7,剩余目标值 0(找到有效组合 [7])
13. 最终结果为 [[2,2,3], [7]]