https://leetcode.com/problems/combination-sum/
题意:给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
思路:经典的回溯法。先排序,从头开始尝试将candidates中每个数加入tmplist,在递归算法中设置条件,若超过target则返回,等于则将结果写入res,小于则继续添加。
类似题目:
[40. Combination Sum II](元素不能重复取)
(https://leetcode.com/problems/combination-sum-ii/)
class Solution:
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
res = [] #结果集
tmpList = [] #工作list
candidates.sort() #先排序
self.backtrack(res, tmpList, candidates, target, 0)
return res
#回溯函数,remain为当前与target的差距,start为候选集中的起始位置
def backtrack(self, res, tmpList, candidates, remain, start):
if remain < 0: #超了,返回
return
elif remain == 0: #恰好等于,记录结果,并返回
res.append(list(tmpList)) #创建一个新的list再写入,否则res会随tmplist改变
return #其实不返回也没事,因为函数到这之后也会退出
else: #还不够,继续递归
for i in range(start, len(candidates)): #从start开始,对每个数遍历
tmpList.append(candidates[i]) #尝试先加入tmplist
self.backtrack(res, tmpList, candidates, remain - candidates[i], i) #递归,注意最后一个参数不是i+1,因为元素可以重复使用
tmpList.remove(tmpList[len(tmpList) - 1]) #不符合则回退
改进:其实结果集res可以作为全局变量,不用在递归中传递
class Solution:
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
self.res = [] #全局记录
tmpList = []
candidates.sort()
self.backtrack(tmpList, candidates, target, 0)
return self.res
def backtrack(self, tmpList, candidates, remain, start):
if remain < 0:
return
elif remain == 0:
self.res.append(list(tmpList))
return
else:
for i in range(start, len(candidates)):
tmpList.append(candidates[i])
self.backtrack(tmpList, candidates, remain - candidates[i], i)
tmpList.remove(tmpList[len(tmpList) - 1])