给定一个数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
39.candidates
中的数字可以无限制重复被选取。
backtrack步骤如下:
- 剪枝:如果当前tmp数组的和cur已经大于目标target,没必要枚举了,直接return
- 如果当前tmp数组的和cur正好和目标target相等,找到一个组合,加到结果res中去,并return
- for循环遍历从index开始的数,选一个数进入下一层递归。因为数可以重复,所以在进入下一层递归时,i没有加1,当前数candidates[i]可以再次被选择
class Solution:
def combinationSum(self, candidates: List[int],
target: int) -> List[List[int]]:
def backtrack(temp, cur, index):
if cur == target:
res.append(temp)
if cur > target:
return
for i in range(index, n):
backtrack(temp + [candidates[i]], cur + candidates[i], i)
res = []
n = len(candidates)
backtrack([], 0, 0)
return res
40.candidates
中的每个数字在每个组合中只能使用一次。
数组 candidates
有序,也是 深度优先遍历 过程中实现「剪枝」的前提。
将数组先排序的思路来自于这个问题:去掉一个数组中重复的元素。
先对数组 升序 排序,重复的元素一定不是排好序以后相同的连续数组区域的第 1 个元素。也就是说,剪枝发生在:同一层数值相同的结点第 2、3 ... 个结点,因为数值相同的第 1 个结点已经搜索出了包含了这个数值的全部结果,同一层的其它结点搜索出的结果一定不会比第 1 个结点更多,并且是第 1 个结点的子集。
class Solution:
def combinationSum2(self, candidates: List[int],
target: int) -> List[List[int]]:
def backtrack(temp, cur, index):
if cur == target:
res.append(temp)
if cur > target:
return
for i in range(index, n):
if i > index and candidates[i] == candidates[i - 1]: continue
backtrack(temp + [candidates[i]], cur + candidates[i], i+1)
res = []
candidates.sort()
n = len(candidates)
backtrack([], 0, 0)
return res