力扣Hot100题目解析:39. 组合总和(Java详解)
一、题目描述
给定一个无重复元素的数组 candidates 和一个目标数 target,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
示例:
输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
二、解题思路(回溯+剪枝)
这道题用回溯法(递归+回退)来实现所有可能组合的枚举。
思路详解:
- 排序 candidates 数组(便于后续剪枝优化)。
- 使用递归函数(回溯)枚举所有可能的组合:
- 每次递归传入当前的组合列表、剩余的 target 以及当前遍历的起始下标。
- 如果 target == 0,说明找到一个合法组合,将其加入答案列表。
- 如果 target < 0,说明当前组合不合法,直接返回。
- 递归遍历 candidates,从当前 index 开始(允许元素重复选取),每次选择当前元素并递归下去。
- 剪枝优化:如果 candidates 已排序,遇到 candidates[i] > target 时,可以直接 break。
这样可以高效地枚举所有可能的解。
三、Java代码实现(详细注释)
import java.util.*;
public class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>(); // 存放最终结果
Arrays.sort(candidates); // 排序,便于剪枝
backtrack(candidates, target, 0, new ArrayList<>(), res);
return res;
}
// 回溯函数
private void backtrack(int[] candidates, int target, int start, List<Integer> path, List<List<Integer>> res) {
// 如果 target 为0,说明找到一个结果
if (target == 0) {
res.add(new ArrayList<>(path)); // 注意要复制一份
return;
}
// 遍历所有候选数
for (int i = start; i < candidates.length; i++) {
// 剪枝:如果当前数比剩余target大,后面更大,无需再试
if (candidates[i] > target) break;
// 选择当前数
path.add(candidates[i]);
// 因为可以重复选取,所以递归时start仍为i
backtrack(candidates, target - candidates[i], i, path, res);
// 回退,撤销选择
path.remove(path.size() - 1);
}
}
}
代码详解:
Arrays.sort(candidates)
:排序方便剪枝。backtrack
递归函数:每次选择一个数,递归搜索剩余部分。- 剪枝优化:遇到 candidates[i] > target 时直接跳出循环。
- 路径回退:每次递归完后要撤销上一次的选择。
四、总结
回溯法是解决组合类、排列类问题的常用技巧,理解递归和剪枝思想可以帮助你高效解决类似问题。
文章标签: 力扣,算法,Java,回溯,剪枝,面试,数组,递归