一、
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
**注意:**解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
提示:
-
1 <= candidates.length <= 100 -
1 <= candidates[i] <= 50 -
1 <= target <= 30
二、
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> combine = new ArrayList<>();
Arrays.sort(candidates);
List<int[]> numList = new ArrayList<>(); //用于存储数组中每个单独数字出现的次数
for (int candidate : candidates) {
int size = numList.size();
if (numList.isEmpty() || numList.get(size - 1)[0] != candidate) {
numList.add(new int[]{candidate, 1});
} else {
++numList.get(size - 1)[1];
}
}
dfs(res, combine, numList, target, 0);
return res;
}
public void dfs(List<List<Integer>> res, List<Integer> combine, List<int[]> numList, int target, int idx) {
// 满足条件加入结果集
if(target == 0){
res.add(new ArrayList<>(combine));
return;
}
// 不满足条件直接进入下一次循环
if(idx == numList.size() || numList.get(idx)[0] > target){
return;
}
// 不取当前位置的数字, 直接下一个数字
dfs(res, combine, numList, target, idx+1);
int most = Math.min(target / numList.get(idx)[0], numList.get(idx)[1]);
for (int i = 1; i <= most; i++) {
combine.add(numList.get(idx)[0]);
// 取了i次当前位置数字, 每取一次需要去判断一次能不能满足
dfs(res, combine, numList, target - i*numList.get(idx)[0], idx+1);
}
for (int i = 0; i < most; i++) {
// 最终需要移除掉上面加入的当前位置数字
combine.remove(combine.size()-1);
}
}
}
这段代码是一个Java类Solution,它包含了一个用于解决组合总和问题的方法combinationSum2以及一个辅助的递归方法dfs。下面我将逐行解释这段代码:
-
public List<List<Integer>> combinationSum2(int[] candidates, int target) {这是combinationSum2方法的定义,它接受两个参数,一个整数数组candidates和一个整数target,并返回一个列表,其中包含满足特定条件的组合。 -
List<List<Integer>> res = new ArrayList<>();在这一行,创建了一个名为res的列表,用于存储最终的结果,即满足条件的组合。 -
List<Integer> combine = new ArrayList<>();创建了一个名为combine的列表,用于在递归中存储当前的组合。 -
Arrays.sort(candidates);对输入的candidates数组进行排序,以便在后续的组合生成中更容易处理。 -
List<int[]> numList = new ArrayList<>();创建一个名为numList的列表,用于存储数组中每个唯一数字以及它们出现的次数。 -
接下来的循环用于填充
numList,它遍历candidates数组并记录每个数字的出现次数。 -
dfs(res, combine, numList, target, 0);调用递归方法dfs来开始解决组合总和问题,并将结果存储在res中。 -
public void dfs(List<List<Integer>> res, List<Integer> combine, List<int[]> numList, int target, int idx) {这是递归方法dfs的定义,它接受多个参数,包括结果列表res、当前组合combine、数字列表numList、目标值target和当前处理的数字索引idx。 -
if (target == 0) {这个条件检查是否已经找到一个满足条件的组合,如果target等于0,表示已经找到一个合法的组合,将其添加到结果列表中。 -
if (idx == numList.size() || numList.get(idx)[0] > target) {这个条件检查是否需要继续递归生成组合。如果idx等于numList的大小或者numList中的当前数字大于target,则没有必要继续递归。 -
dfs(res, combine, numList, target, idx + 1);这一行调用dfs方法,跳到下一个数字的位置,即不取当前位置的数字。 -
int most = Math.min(target / numList.get(idx)[0], numList.get(idx)[1]);计算可以取当前位置数字的最大次数,以便在循环中生成不同的组合。 -
接下来的循环用于生成不同次数的当前位置数字的组合,然后递归进一步处理。这部分代码实际上处理了组合总和问题的核心逻辑。
-
最后一个循环用于移除
combine中上面添加的当前位置数字,以便继续生成其他组合。
三、
- 输入: candidates = [10,1,2,7,6,1,5], target = 8
以下是针对输入candidates = [10, 1, 2, 7, 6, 1, 5]和target = 8的代码运行过程的详细描述:
-
首先,将候选数组
candidates排序,得到[1, 1, 2, 5, 6, 7, 10],以方便后续处理。 -
创建一个名为
numList的列表,用于存储每个数字及其出现次数。在这个例子中,numList将包含以下内容:cssCopy codenumList = [[1, 2], [2, 1], [5, 1], [6, 1], [7, 1], [10, 1]] -
然后,调用
dfs方法,开始递归处理组合总和问题。初始调用如下:scssCopy codedfs(res, combine, numList, 8, 0) -
进入
dfs方法,首先检查是否已经找到一个满足条件的组合。此时target等于8,尚未满足条件。 -
然后,检查是否需要继续递归生成组合。由于
idx等于0,且numList.get(0)[0]等于1,小于等于8,因此需要继续递归。 -
进入循环,首先处理取0次1的情况:
cssCopy codecombine = [], target = 8, idx = 1 -
接下来处理取1次1的情况:
cssCopy codecombine = [1], target = 7, idx = 1 -
继续递归,处理取0次2的情况:
cssCopy codecombine = [1], target = 7, idx = 2 -
接下来处理取1次2的情况:
cssCopy codecombine = [1, 2], target = 5, idx = 2 -
继续递归,处理取0次5的情况:
cssCopy codecombine = [1, 2], target = 5, idx = 3
-
接下来处理取1次5的情况:
cssCopy codecombine = [1, 2, 5], target = 0, idx = 3
-
满足条件,将组合
[1, 2, 5]添加到结果列表res中。 -
回溯到上一层,移除最后一个数字5,变为:
cssCopy codecombine = [1, 2], target = 5, idx = 3 -
然后处理取2次5的情况:
cssCopy codecombine = [1, 2, 5, 5], target = -5, idx = 3 -
不满足条件,继续回溯到上一层,移除最后一个数字5。
-
回溯到上一层,移除数字2,变为:
cssCopy codecombine = [1], target = 7, idx = 2 -
接着处理取0次2的情况:
cssCopy codecombine = [1], target = 7, idx = 3 -
继续处理取1次2的情况,以及取0次5和1的情况。
-
继续递归的过程会依次处理各种组合情况,直到找到所有满足条件的组合为止。
-
最终,
res中包含以下组合:csharpCopy code[ [1, 1, 6], [1, 2, 5], [1, 7], [2, 6] ]
这些组合的数字总和等于目标值8。整个过程是通过深度优先搜索(DFS)实现的,递归探索了所有可能的组合情况。