题目:
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例:
输入:candidates = [2,3,6,7], target = 7,
所求解集为: [ [7], [2,2,3] ]
思路:
回溯法,这里分剪枝和不剪枝两种方法,剪枝可以降低计算复杂度,使计算过程更简化。
不剪枝过程,目的是在数组中找到所有和为target的组合,这个数组中的数可以重复利用,但是解的集合中不能包括重复的解。
1.比如目标是7,数组是[2,3,6,7],来数组中找,每一次找都可以用2,3,6,7这四个数,先找2,用上2了,7-2为5,还差5,还去数组中找,依次找,用上2,5-2为3,还差3,还去数组中找,依次找,还是用2,还差1,这次找1,能找到1的话,就找到一条路径了,如果结果小于0,那这条路径肯定不满足,如果结果大于0,我们还能在数组中找,现在来看,数组中首选也就是第一位的是2,1-2是负值了,这条路径肯定不行,再找3,6,7发现都不行。
2.再往上一层级上返回,差3,用完2了,用3,发现正好满足,2+2+3,返回一条路径,这样2和3都用完了,用后边的6,7看是否能得到3,发现都不行。
3.再往上一级返回,一开始用2,差5,为得到5找了2,现在找3,那就需要5-3为2了,这里要注意,前面2的时候找了3,这里3的时候就不要找2了,避免结果的重复,然后就找3,6,7,发现2减去这几个数都是负值,就不行了。
4.同样思路遍历第二层里的6,7,第一层里的3,6,7.
可以看看题解里的图,很清晰。
代码解析,长度为0,直接返回空,开后调用函数,进入循环,结果小于0,返回,看下一数,结果等于0,在结果中加入这一路径,看下一个数,要注意同级中,前面遍历过的数,后面不遍历了,是通过最内层循环里索引i作为再调用dfs函数的开始位置来实现的。
剪枝法,可以先将数组中的数由小到大排序,如果前面的数不满足情况,后面的更不满足,就可以直接返回,进行上一层级的遍历。
class Solution(object):
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
#不剪枝,不需要排序,深度优先遍历,回溯法,
if len(candidates)==0:
return []
def dfs(candidates,begin,size,res,path,target):
if target<0:
return
if target==0:
res.append(path)
return
for i in range(begin,size):
dfs(candidates,i,size,res,path+[candidates[i]],target-candidates[i])
size = len(candidates)
path = []
res = []
dfs(candidates,0,size,res,path,target)
return res
# 解法2:
# 剪枝,排序,当前面的小于0时就不用遍历后边的了,肯定不成立
if len(candidates)==0:
return []
def dfs(candidates,begin,size,res,path,target):
if target==0:
res.append(path)
return
for i in range(begin,size):
residue = target-candidates[i]
if residue < 0:
break
dfs(candidates,i,size,res,path+[candidates[i]],residue)
size = len(candidates)
path = []
res = []
candidates.sort()
dfs(candidates,0,size,res,path,target)
return res