思路1:回溯
- 定义递归函数
dfs(target, combine, idx)
当前在第idx
位,还剩target
要组合,已经组合的列表为combine
。
递归的终止条件为 target <= 0
或者 数组被全部用完。 - 在当前的函数中,每次我们可以选择跳过不用第 idx 个数,即执行
dfs(target, combine, idx + 1)
。 - 也可以选择使用第 idx 个数,即执行
dfs(target - candidates[idx], combine, idx),
- 每个数字可以被无限制重复选取,因此搜索的下标仍为
idx
。
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
def dfs(target,idx):
if idx==len(candidates):
return
if target==0:
ans.append(combine[:])
return
#跳过当前数
dfs(target,idx+1)
#使用当前数
if target-candidates[idx]>=0:
combine.append(candidates[idx])
dfs(target-candidates[idx],idx)
combine.pop()
ans=[]
combine=[]
dfs(target,0)
return(ans)
思路2:另一种回溯写法:
- 搜索至任何一个节点时,总是会先判断当前节点是否可以通往最后的合法解。
- 如果不可以,则结束对「以当前节点为根节点的子树」的搜索,向父节点回溯,回到之前的状态,搜索下一个分支。
- 否则,进入该子树,继续以DFS的方式搜索。
注意:
- 空间树中的节点是动态的,即,当前有哪些选项可选择,是根据上一步的选择得出的,所以做回溯时,要把状态还原成进入当前节点之前的状态。
- 设置begin参数,即dfs是从当前节点向后搜索,避免重复
#加法
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
def dfs(i,path):#i即begin节点
if sum(path) == target:
ans.append(path[:])
if sum(path) > target:
return
for j in range(i, len(candidates)):
path.append(candidates[j])#加入节点
dfs(j, path)#判断是否合适,接着向下搜索
path.pop()#还原状态
ans=[]
dfs(0,[])
return(ans)
另一种写法:时间和内存会好一点
#减法
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
def dfs(begin, target):
if target < 0:
return
if target == 0:
ans.append(combine[:])
return
for index in range(begin, len(candidates)):
residue = target - candidates[index]
combine.append(candidates[index])
dfs(index, residue)
combine.pop()
combine=[]
ans=[]
dfs(0,target)
return ans