这道题必须去重,去重又需要先进行排序。
当然可以!下面是一份适用于 LeetCode 40(组合总和 II)的**“思路总览”模板答案**,适合写在笔试或面试中,逻辑清晰、结构规范、面试官友好:
✅ 题目思路总览:LeetCode 40. 组合总和 II
🧩 题目理解:
给定一个可能包含重复数字的数组 candidates
,和一个目标数 target
,要求找出所有不重复的组合,使得组合中元素的和为 target
,每个数字只能使用一次。
🔍 解题思路:
1. 排序 + 回溯
- 首先对
candidates
进行升序排序,方便后续去重和剪枝操作。 - 使用回溯算法(Backtracking)探索所有可能的组合路径。
- 每一步选择当前数,并递归向后查找剩余目标
remain - candidates[i]
。
2. 去重处理
- 避免相同元素在同一层递归中出现两次,使用:
if (i > start && candidates[i] == candidates[i - 1]) continue;
- 该逻辑必须建立在数组有序的前提下。
3. 剪枝优化
- 如果当前元素已经大于剩余值
remain
,由于数组有序,后续也无需继续搜索,直接break
。
⏱ 时间复杂度分析(近似估算):
- 最坏情况下,回溯会遍历所有子集,时间复杂度接近 O(2^n),但因为有剪枝+去重,实际远小于此。
- 空间复杂度为 O(n)(递归栈 + 路径空间)
✅ 总结关键词:
- 回溯(Backtracking)
- 排序 + 去重
- 剪枝优化
- 每个元素只能用一次(控制递归起点)
java solution
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList<>();
//因为需要去重, 所以首先必须排序
Arrays.sort(candidates);
backtrack(0, target, new ArrayList<>(), candidates, result);
return result;
}
private void backtrack(int start, int remain, List<Integer> list,int[] candidates, List<List<Integer>> result) {
//先判断是否满足期望条件
if(remain == 0) {
result.add(new ArrayList<>(list));
return;
}
for(int i = start; i < candidates.length; i++) {
//因为题目要求不能有重复元素, 所以需要首先去重
if(i > start && candidates[i] == candidates[i - 1]) continue;
//剪枝
//之所以用的是 break 而不是 continue,关键点在于已经对 candidates 做了排序。
//当你遇到一个数 candidates[i] > remain,说明这个数已经比剩余目标值还大了:
//那么 后面更大的数也一定无法组成合法组合
//所以,继续遍历没有意义,可以直接终止循环
if(candidates[i] > remain) break;
//添加一个元素
list.add(candidates[i]);
//回溯判断
backtrack(i + 1, remain - candidates[i], list, candidates, result);
//撤销
list.remove(list.size() - 1);
}
}
}