【leetcode刷题】回溯算法-附python代码

回溯算法的理解

算法理解

回溯算法,其实就是指导遍历一棵多叉树。
回溯函数中含有循环和递归,循环是控制树的横向遍历,递归是控制树的纵向遍历,其本质是深度优先搜索。
一般用于排列组合问题。

回溯函数的一般结构

def backtracking(一些参数):# ① 参数的设置,否设置start_index 参数
	if 循环终止条件:
		收集结果
		return
	for i in range(层序循环):# ②循环空间的设置(含一些剪枝操作)
		一些剪枝操作# ③有时候仅在循环处就可以控制剪枝操作)
		加入节点,及其相关操作 
		backtracking(一些参数) # ④start_index参数是否需要改变
		弹出节点,及其相关操作

回溯算法要点

  1. 把问题变成一棵树
  2. 确定循环空间的设置
  3. 确定加入和弹出节点有哪些操作
  4. 确定循环终止条件
  5. 剪枝策略

真题演练

77.组合

combinations
在这里插入图片描述
剪枝策略: n − x + 1 > = k − l e n ( p a t h ) n-x+1>=k-len(path) nx+1>=klen(path),然后python循环的经典+1

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        path=[]
        res=[]
        def backtracking(n,k,start_index):
            if len(path)==k:
                res.append(path[:])
                return
            for i in range(start_index,n+2-k+len(path)):
                path.append(i)
                backtracking(n,k,i+1) # 注意,这里是i 不是start_index
                path.pop()
            
        backtracking(n,k,1)
        return res

216 组合总和Ⅲ

https://leetcode.cn/problems/combination-sum-iii/
树和上个题一样,终止条件不同

class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        res = []
        path = []
        self.sum_now=0


        def backtracking(k: int, n: int, startindex: int):
            if len(path) == k:
                if sum(path) == n:
                    res.append(path[:])
                return
            for i in range(startindex,11-k+len(path)):
                if self.sum_now > n:
                    break
                path.append(i)
                self.sum_now+=i
                backtracking(k, n, i + 1)
                path.pop()
                self.sum_now-=i

        backtracking(k, n, 1)
        return res

17.电话号码的字母组合

https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
在这里插入图片描述
因为列表间不重复,所以不需要start_index 这个东西

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        n=len(digits)
        if n==0:
            return []
        phone={
    
    2:'abc',3:'def',4:'ghi',5:'jkl',6:'mno',7:'pqrs',8:'tuv',9:'wxyz'}
        path=[]
        res=[]
        
        def backtracking(digits,n,start_index):
            if len(path)==n:
                res.append("".join(path[:]))
                return
            for i in phone[int(digits[start_index])]:
                path.append(i)
                backtracking(digits,n,start_index+1)
                path.pop()
        
        backtracking(digits,n,0)
        return res

39. 组合总和

循环的每一层用的同一个list,所以需要start_index作为参数传入,
这个题用过的数字还可以再用,故回溯时候不用 i+1,
sort()一下方便剪枝,会更快。

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        path=[]
        res=[]
        self.sum=0
        n=len(candidates)
        candidates.sort()

        def bcaktracking(candidates,n,target,start_index):
            if self.sum==target:
                res.append(path[:])
                return
            for i in range(start_index,n):
                if self.sum+candidates[i]>target:
                    break
                
                path.append(candidates[i])
                self.sum+=candidates[i]
                bcaktracking(candidates,n,target,i)
                path.pop()
                self.sum-=candidates[i]
                
        bcaktracking(candidates,n,target,0)
        return res

40 组合总和Ⅱ

https://leetcode.cn/problems/combination-sum-ii/
没有去重的后果:
比如输入是[1,1,2,6] target=7
那么会输出[1,2,6],[1,2,6],
但其实两个1是一样的,故是重复的,会报错。

解释一下去重关键步骤:
start_index 其实控制的是层数,
i>start_index 就是指在start_index 这一层的后面其他数字
candidates[i]==candidates[i-1] 表示数值和前一位数值一样(因为事先有过sort 的步骤了)

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        candidates.sort()
        res=[]
        path=[]
        self.sum=0

        def backtracking(candidates,target,start_index):
            if self.sum==target:
                res.append(path[:])
            for i in range(start_index,len(candidates)):
                if self.sum+candidates[i]>target:
                    return
                if i>start_index and candidates[i]==candidates[i-1]:# 去重关键步骤
                    continue
                path.append(candidates[i])
                self.sum+=candidates[i]
                backtracking(candidates,target,i+1)
                path.pop()
                self.sum-=candidates[i]
        
        backtracking(candidates,target,0)
        return res

131. 分割回文串

这个题和前面不一样,如何转化成树也是一个难点。

题目原文
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“a”,“b”] ]

思路:
这个和中学的排列组合题有点像,
切开字符串就相当于在字符串里插入断点/隔板,
每一层分别插入隔板数量分别为1,2,3…,
子节点是在当前状态下,在此隔板的后方再插入隔板。

那么具体操作时,还需判断递归终止条件和剪枝策略。
终止条件:后面不能再放隔板了start_index==len(s),即为终止。
**剪枝:**因为下一层隔板放在第一层最后一个隔板的后面,故对前面的字符串没有改变,故若前面字符temp=s[start_index:i+1]串非回文,就可以剪枝了,此处用的continue

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        n=len(s)
        path=[]
        res=[]

        # def is_palindrome(str):
        #     i, j = 0, len(str)-1
        #     while i < j:
        #         if str[i] != str[j]:
        #             return False
        #         i += 1
        #         j -= 1
        #     return True

        def backtracking(s,start_index):
            if start_index==len(s):
                res.append(path[:])
                return
            for i in range(start_index,len(s)):
                temp=s[start_index:i+1]
                if temp==temp[::-1]: # 判断回文的方式1:字符串倒序看是否一致。
                # if is_palindrome(temp):# 判断回文的方式2:根据定义写函数。
                    path.append(temp)
                    backtracking(s,i+1)
                    path.pop()
                else:
                    continue
        backtracking(s,0)
        return res

93. 复原 IP 地址

https://leetcode.cn/problems/restore-ip-addresses/
和回文子串相同,也是经典的插隔板问题。

题目原文
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “[email protected]” 是 无效的 IP 地址。

猜你喜欢

转载自blog.csdn.net/D2Ooo/article/details/127208694