组合问题,39,40,216,377 回溯,深度优先搜索

在这里插入图片描述
给定一个数组,从数组里找一些数,使这些数字和为目标值,且要求使数组中的这些数不能重复使用(在每个组合里)
所有数字都是正整数,解集不包含重复组合。

参考避免循环链接

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
方法二,完全按照3940题来写
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转化为动态规划背包问题。为了和找零钱,等动态规划问题一起看,所以单独开一个帖子

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值