目录
- 题目
- 答案
- 运行结果
题目
给定一个候选人编号的集合 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
答案
我们可以先对数组进行排序,方便剪枝以及跳过重复的数字。
接下来,我们设计一个函数 dfs(i,s),表示从下标 i 开始搜索,且剩余目标值为 s,其中 i 和 s 都是非负整数,当前搜索路径为 t,答案为 ans。
在函数 dfs(i,s) 中,我们先判断 s 是否为 0,如果是,则将当前搜索路径 t 加入答案 ans 中,然后返回。如果 i≥n,或者 s<candidates[i],说明当前路径不合法,直接返回。否则,我们从下标 i 开始搜索,搜索的下标范围是 j∈[i,n),其中 n 为数组 candidates 的长度。在搜索的过程中,如果 j>i 并且
candidates[j]=candidates[j−1],说明当前数字与上一个数字相同,我们可以跳过当前数字,因为上一个数字已经搜索过了。否则,我们将当前数字加入搜索路径 t 中,然后递归调用函数 dfs(j+1,s−candidates[j]),然后将当前数字从搜索路径 t 中移除。
我们也可以将函数 dfs(i,s) 的实现逻辑改为另一种写法。如果我们选择当前数字,那么我们将当前数字加入搜索路径 t 中,然后递归调用函数 dfs(i+1,s−candidates[i]),然后将当前数字从搜索路径
t 中移除。如果我们不选择当前数字,那么我们可以跳过与当前数字相同的所有数字,然后递归调用函数 dfs(j,s),其中 j 为第一个与当前数字不同的数字的下标。
在主函数中,我们只要调用函数 dfs(0,target),即可得到答案。
时间复杂度 O(2 n ×n),空间复杂度 O(n)。其中 n 为数组 candidates 的长度。由于剪枝,实际的时间复杂度要远小于
O(2 n ×n)。
class Solution(object):def combinationSum2(self, candidates, target):""":type candidates: List[int]:type target: int:rtype: List[List[int]]"""def dfs(i, s):if s == 0:ans.append(t[:])returnif i >= len(candidates) or s < candidates[i]:returnfor j in range(i, len(candidates)):if j > i and candidates[j] == candidates[j - 1]:continuet.append(candidates[j])dfs(j + 1, s - candidates[j])t.pop()candidates.sort()ans = []t = []dfs(0, target)return ans
class Solution(object):def combinationSum2(self, candidates, target):""":type candidates: List[int]:type target: int:rtype: List[List[int]]"""def dfs(i, s):if s == 0:ans.append(t[:])returnif i >= len(candidates) or s < candidates[i]:returnx = candidates[i]t.append(x)dfs(i + 1, s - x)t.pop()while i < len(candidates) and candidates[i] == x:i += 1dfs(i, s)candidates.sort()ans = []t = []dfs(0, target)return ans