代码随想录day25 回溯(2)

39. 组合总和 - 力扣(LeetCode)  

解一:倒序排列candidates数组。对当前元素cur,求在不超出target的前提下最多出现次数rep,遍历所有可能的出现次数c;对当前c,将c个cur放入path,递归调用backtrace并传入cur的下一个元素和target-cur*c,每次调用backtrace都回溯path(target的回溯隐含在参数传递中)。递归退出条件是遍历完整个数组或达到target==0。由于对于每个元素,都可以选择c=0,即不将它放入path,隐含了对start在len(candidates)上的遍历,因此不需要再遍历range(start, len),否则会造成重复。

class Solution:
    def __init__(self):
        self.path = []
        self.res = []
    
    def backtrace(self, start, candidates, target):
        if target == 0:
            self.res.append(self.path[:])
            return
        if start >= len(candidates):
            return
        
        cur = candidates[start]
        rep = target // cur #最大可重复次数
        for c in range(rep, -1, -1): #cur可以出现rep, ..., 1, 0次
            self.path += [cur] * c
            self.backtrace(start+1, candidates, target-c*cur)
            for _ in range(c):
                self.path.pop()

    def combinationSum(self, candidates, target):
        candidates.sort()
        candidates.reverse()
        self.backtrace(0, candidates, target)
        return self.res

 解二:排序数组candidates后,遍历[start, len),若当前元素candidates[i]不超过target,加入path,递归调用traceback并传入i和target-candidates[i],随后回溯。这里通过传入i(当前元素下标)而不是i+1(下一个元素下标)允许了数字重复。

class Solution:
    def __init__(self):
        self.path = []
        self.res = []
    
    def backtrace(self, start, candidates, target):
        if target == 0:
            self.res.append(self.path[:])
            return
        
        for i in range(start, len(candidates)):
            if candidates[i] <= target:
                self.path.append(candidates[i])
                self.backtrace(i, candidates, target-candidates[i])
                self.path.pop()

    def combinationSum(self, candidates, target):
        candidates.sort()
        self.backtrace(0, candidates, target)
        return self.res

40. 组合总和 II - 力扣(LeetCode) 

难点是去重操作。尝试:在target==0、res.append之前加一个判断:if self.path in self.res,但是会超时。

在搜索答案的时候,首先确定path的第一个元素(对应树的第一层),再次调用traceback,进入树的下一层,也就是path下一个位置的元素...(以上操作沿着树垂直向下)直到达到递归中止条件,path回退末尾元素(垂直向上),平行向右移动搜索path当前位置的其他可能值,重复。去重是指,在数组已排序的前提下,path同一个位置(或说树的同一层)不可以重复出现一样的元素。

解法:同层的元素共享一个范围 candidates[start : -1],只需比较当前元素是否与同层的上一个(它左边的)元素相等,若相等,为避免重复,跳过此元素,查看下一个元素。

class Solution(object):
    def __init__(self):
        self.path = []
        self.res = []

    def traceback(self, candidates, start, target):
        if target == 0:
            self.res.append(self.path[:])
            return

        for i in range(start, len(candidates)):
            if candidates[i] > target:
                break
            if i > start and candidates[i-1] == candidates[i]:
                #去重:当i不是这一层的首个元素而且与这一层已经访问过的元素相同,就跳过这个i
                continue

            self.path.append(candidates[i])
            self.traceback(candidates, i+1, target-candidates[i])
            self.path.pop()

    def combinationSum2(self, candidates, target):
        candidates.sort() #[1, 1, 2, 5, 6, 7, 10]
        self.traceback(candidates, 0, target)
        return self.res

131. 分割回文串 - 力扣(LeetCode) 

class Solution(object):
    def __init__(self):
        self.res = []
        self.path = []
    
    def dp(self, s):
        for i in range(len(s)-1, -1, -1):
            for j in range(i, len(s)):
                if i == j:
                    self.isval[i][j] = True
                elif i+1 == j:
                    self.isval[i][j] = s[i] == s[j]
                else:
                    self.isval[i][j] = s[i] == s[j] and self.isval[i+1][j-1]

    def bt(self, s, start):
        if start >= len(s):
            self.res.append(self.path[:])
            return
        
        for i in range(start, len(s)):
            if self.isval[start][i]:
                self.path.append(s[start:i+1])
                self.bt(s, i+1)
                self.path.pop()
            

    def partition(self, s):
        self.isval = [[False] * len(s) for _ in range(len(s))]
        self.dp(s)
        self.bt(s, 0)
        return self.res

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值