给定一个数组,从数组里找一些数,使这些数字和为目标值,且要求使数组中的这些数不能重复使用(在每个组合里)
所有数字都是正整数,解集不包含重复组合。
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
end = len(candidates)#begin,end搜索的起止索引位置
if end == 0:
return []
candidates.sort() #剪枝,对数组进行从小到大排序
res = []
def dfs(begin, path, target): #回溯的dfs,可以看作一个东西
if target == 0:#目标值是不断做减法,为0表示到达目标
res.append(path[:]) #将经过的路径加进去,注意因为是引用对象,所以对path进行拷贝,path[:]
return #套路就是在这return的;满足结束条件,添加路径,return
for index in range(begin, end): #for 选择 in 选择列表
if candidates[index] > target:
break #因为是升序,如果index对应的数组值大于目标,之和的也都大于,所以break
if index > begin and candidates[index - 1] == candidates[index]:
continue#注意,这个是为了使同层的重复元素去掉,同时使下一层的重复元素失效,避免了重复
path.append(candidates[index])#如果无上述两种情况,表明符合路径,添加到路径里
dfs(index+1, path, target - candidates[index])#同时继续向下搜索,因为是每个数字在每个组合里只使用一次,所以index+1,不从index开始索引
path.pop()#同时撤销选择
dfs(0, [], target)#深度优先搜索的初始化
return res
和40题的区别就在于数组里的元素可以重复使用,target=7,可以用7个1表示。
代码区别点就在于继续下一个深度优先搜索时,包括了从index开始,让自己再把自己算进去一遍,就可以做到数组内的元素重复使用。
同时,因为是可以重复使用,所以一般数组里也不会有重复数字,所以40题里第2个判定条件可以省略
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
end = len(candidates)
if end == 0:
return []
candidates.sort()
res = []
def dfs(begin, path, target):
if target == 0:
res.append(path[:])
return
for index in range(begin, end):
if candidates[index] > target:
break
# if index > begin and candidates[index - 1] == candidates[index]:
# continue
path.append(candidates[index])
dfs(index, path, target - candidates[index])
path.pop()
dfs(0, [], target)
return res
前面两题是给定数组,和一个目标值,找出数组里加和为目标值的所有组合,这道题是给定目标值,以及限定从1-9里找出k个数,使这k个数的和为目标值。要求数字不能重复使用,所以从index+1开始
和上面2道题完全类似,写法见方法二
外部先定义边界条件;
然后开启递归,递归里面定义递归出口==target或len(path)==k and target;然后写递归break的地方,然后写递归内部内容;
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
res = []
def helper(begin,path,target):
if(len(path) == k and target==0):#满足k个数,且目标减到了0
res.append(path[:])
return#满足结束条件,添加路径,return
for index in range(begin,10):#for 选择 in 选择列表
path.append(index)#做选择
helper(index+1,path,target-index)#要求数字不能重复使用,所以从index+1开始
path.pop()#撤销选择
helper(1,[],n)
return res
方法二,完全按照39,40题来写
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
res = []#2外部边界条件判断,本题没有
def helper(begin,path,target):#递归函数
if(len(path) == k and target==0):#满足k个数,且目标减到了0 ##222递归出口222
res.append(path[:])
return#满足结束条件,添加路径,return
#if index > target: ###222SyntaxError: 'break' outside loop 注意break要写在循环里面
# break #222递归break的地方222
for index in range(begin,10):#for 选择 in 选择列表 #222递归内容222
if index > target:
break
path.append(index)#做选择
helper(index+1,path,target-index)#要求数字不能重复使用,所以从index+1开始
path.pop()#撤销选择
helper(1,[],n)
return res
组合问题第4题,给定一个数组,找出数组中和为目标值的所有组合,返回的是所有组合的个数。
等同于39题,数组里的数字也可以被无限重复使用,不同点在于一个是所有组合情况,一个是所有组合情况的个数//还不一样,不是单纯的返回长度。
[1,2,3] 目标值为4,按照39题的写法,返回的是[[1, 1, 1, 1], [1, 1, 2], [1, 3], [2, 2]];而377题里,是即使元素相同,顺序不一样也是一个新的组合,39题里只考虑元素一样不一样,是不是一个新的组合,没有考虑元素的顺序变了也是一个新的组合。
所以377转化为动态规划背包问题。为了和找零钱,等动态规划问题一起看,所以单独开一个帖子