给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum
解题思路
借助深度遍历的思想
根节点是目标数, 每往下一层就是减去某个数
可以将给定数组进行排序, 有助于"剪枝"
每个节点有一或多个子节点, 这些子节点从左到右是父节点依次减去排序后的数组中的元素
所以当检查到减去某个元素后结果为负数, 则可以直接忽略掉该元素以及比它大的元素, 实现"剪枝"提高效率
当检查到减去某个元素后刚好为0, 则从该节点往上一直到根节点这段路径就是结果集中的一个解
当检查到减去某个元素后大于0, 则继续往下深度遍历
代码
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList<>();
if (candidates == null || candidates.length == 0) {
return result;
}
// 对给定数组进行排序是"剪枝"的必要步骤
Arrays.sort(candidates);
// 保存深度遍历时路径
Deque<Integer> path = new LinkedList<>();
dfs(candidates, 0, target, result, path);
return result;
}
/**
* @param candidates 给定数组
* @param begin 从数组哪个位置开始
* @param target 目标结果
* @param result 结果集合
* @param path 深度遍历时的路径
* @return void
*/
private void dfs(int[] candidates, int begin, int target, List<List<Integer>> result, Deque<Integer> path) {
// 如果到达该层时目标数已经减为0, 说明此时路径中的数就是符合要求的结果集
if (target == 0) {
result.add(new ArrayList<>(path));
} else { // 否则要继续往下深度遍历
for (int i = begin; i < candidates.length; i++) {
int candidate = candidates[i];
// 如果目标减去当前遍历到的数为负数
if (target - candidate < 0) {
// "剪枝"
break;
}
// 将当前元素加到路径中
path.addLast(candidate);
// 递归传参 begin = i, 则是为了下一层遍历仍从本层的同个数字开始
dfs(candidates, i, target - candidate, result, path);
// 返回上一层时要移出刚添加到路径的那个元素
path.removeLast();
}
}
}
}