算法47--回溯法4--算法总结

之前用回溯法解决了子集,排列,组合等问题,现在总结一下回溯算法:

在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

回溯法实际是对于一颗树的深度优先搜索,在每次搜索一种状态时都要判断该种状态是否满足条件,如果已经不满足,则可以直接剪枝处理;如果当前状态不满足,下一个状态可能满足,则继续搜寻下一个状态。

其实现要点如下:

1.根节点的确定

2.从某一节点出发的子节点的可能情况

3.判断当前状态是否符合条件

4.判断是否可以剪枝

5.根据当前节点的子节点情况进行遍历,选取第一个子节点,保存下一个状态,递归下一个状态判断,回退到当前状态,进行第二个节点判断,依次进行直到所有子节点遍历完成

6.去重的判断:一般将某一节点的所有子节点排序,然后遍历某一子节点,如果前一个节点与本节点相同,则可以直接跳过,搜寻下一个子节点

Given a set of distinct integers, nums, return all possible subsets (the power set).

class Solution:
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        rr=[]
        self.backsubsets(rr, [], 0, nums)   
        return rr
    
    def backsubsets(self, rr, r, index, nums):
        rr.append(r.copy())
        print(rr)
        for i in range(index, len(nums)):
            r.append(nums[i])
            self.backsubsets(rr, r, i+1, nums)
            r.pop(-1)
        

深度优先搜索树如下所示:

Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).

class Solution:
    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        rr=[]
        nums.sort()
        self.backtrack(rr, [], 0, nums)
        return rr
    
    def backtrack(self, rr, r, index, nums):
        rr.append(r.copy())
        for i in range(index, len(nums)):
            if i>index and nums[i]==nums[i-1]:
                continue
            r.append(nums[i])
            self.backtrack(rr, r, i+1, nums)
            r.pop(-1)

要注意去重,当遍历到某一节点,分析其子节点的各自取值时,由于原数组已经排序,因此相同元素此时已经相邻,分析第一个子节点直接进入下一个状态判断,分析第二个子节点时,如果第二个子节点和第一个相同,则应该直接跳过第二个子节点,因此去重的判断条件是

i>index and nums[i-1]==nums[i]

深度优先搜索树:

Given a collection of distinct integers, return all possible permutations.

class Solution:
    def permute(self, nums=[1,2,3]):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        rr = []
        self.backtrace(rr, [], nums)
        return rr
    
    def backtrace(self, rr, r, nums):
        if len(r)==len(nums):
            rr.append(r.copy())
            return
        for i in range(0, len(nums)):
            if nums[i] in r:
                continue
            r.append(nums[i])
            self.backtrace(rr, r, nums)
            r.pop(-1)

深度优先搜索树:

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

class Solution:
    def permuteUnique(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        rr = []
        flag = [False for v in range(len(nums))]
        nums.sort()
        self.backtrace(rr, [], flag, nums)
        return rr
    
    def backtrace(self, rr, r, flag, nums):
        if len(r)==len(nums):
            rr.append(r.copy())
            return
        for i in range(0, len(nums)):
            if flag[i] or (i>0 and nums[i-1]==nums[i] and flag[i-1]):
                continue
            r.append(nums[i])
            flag[i] = True
            #print(r)
            self.backtrace(rr, r, flag, nums)
            r.pop(-1)
            flag[i] = False
        

深度优先搜索树:

Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.The same repeated number may be chosen from candidates unlimited number of times.

class Solution:
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        rr=[]
        self.backtrace(rr, [], 0, candidates, target, 0)
        return rr

    def backtrace(self, rr, r, s, candidates, target, start):
        if s==target:
            rr.append(r.copy())
            return
        if s>target:
            return
        for i in range(start, len(candidates)):
            r.append(candidates[i])
            s += candidates[i]
            self.backtrace(rr, r, s, candidates, target, i)
            s -= candidates[i]
            r.pop(-1)

深度优先树:

Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.Each number in candidates may only be used once in the combination.

class Solution:
    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        rr=[]
        candidates.sort()
        self.backtrace(rr, [], 0, candidates, target, 0)
        return rr
        
    def backtrace(self, rr, r, s, candidates, target, start):
        if s == target:
            rr.append(r.copy())
            return
        if s > target:
            return
        for i in range(start, len(candidates)):
            if i>start and candidates[i-1]==candidates[i]:
                continue
            r.append(candidates[i])
            #print(r)
            s += candidates[i]
            #print(s)
            self.backtrace(rr, r, s, candidates, target, i+1)
            r.pop(-1)
            s -= candidates[i]

Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.

class Solution:
    def combinationSum3(self, k, n):
        """
        :type k: int
        :type n: int
        :rtype: List[List[int]]
        """
        rr=[]
        self.backtrace(rr, [], 0, k, 1, n)
        return rr
        
    def backtrace(self, rr, r, s, k, start, n):
        if len(r)==k and s==n:
            rr.append(r.copy())
            return      
        if s>n or len(r)>k or (len(r)==k and s!=n):
            return                
        for i in range(start, 10):
            r.append(i)            
            s += i
            self.backtrace(rr, r, s, k, i+1, n)
            r.pop(-1)
            s -= i

回溯法实现了对于集合按照某种规则进行遍历所有情况,当遍历到某一种情况时要判断搜寻条件是否符合,以及根据当前节点判断其子节点的情况。

回溯法一般处理步骤:

声明保存所有树节点状态集合rr

声明用来保存搜寻过程中的节点保存变量r

确定根节点,一般从空集开始[]

判断当前状态r是否符合条件,符合则复制r一份加入到rr中

如果不符合,并且继续搜寻子节点也必定不符合,则直接返回

分析当前节点的左右可能子节点:

      根据子节点的取值,判断是否去重,是否直接进行下一个子节点的搜寻

      将子节点加入到r中,更新相关变量

      进行子节点的递归搜寻

      将子节点从r中删除,进行下一个子节点的分析。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值