搜索与回溯

回溯

回溯r如果要展示所有的内容,三步走:设置现场,dfs,恢复现场。
需要这三步走的原因是因为要把所有的可能性都穷举出来。

组合求和 lc 39

given candidate set [2, 3, 6, 7] and target 7,
A solution set is: [[7],[2, 2, 3]]

class Solution:
    def combinationSum(self,candidates,target):
        if not candidates:
            return []

        def dfs(start_index,candidates,target,path,res):
            if target == 0:
                res.append(copy.deepcopy(path))
                return

            for i in range(start_index,len(candidates)):
                if candidates[i]>target:
                    continue
                #设置现场
                path.append(candidates[i])
                #是否能减成0,dfs
                dfs(i,candidates,target-candidates[i],path,res)
                #恢复现场
                path.pop()
        path = []
        res = []
        start_index = 0
        dfs(start_index,candidates,target,path,res)
        return res
  • 注意:有i 还是顺序往下走一次入栈,没有i是所有在都来一次,会出现2,3,3, 3,2,3所谓的顺序

分割等和子集 lc 461

Input: [1, 5, 11, 5] Output: true Explanation: The array can be
partitioned as [1, 5, 5] and [11].

(1)回溯

class Solution:
	def canPartition(self,nums):
        nums_sum = sum(nums)
        if nums_sum%2!=0:
            return False
        else:
            target = nums_sum/2

        def dfs(start_index,nums,target)#,path):
            if target == 0:
                res[0] = True
                return
            for i in range(start_index,len(nums)):
                if nums[i]>target:
                    continue
                #path.append(nums[i])
                dfs(i+1,nums,target-nums[i])#,path)
                #path.pop()
        res=[False]
        path = []
        dfs(0,nums,target)#,path)
        return res[0]
  • 不需要穷举所有结果,只需要有一个正确即可。所以不需要设置现场和恢复现场。
  • 因为不正确不代表说没有,即不能停,但是正确是找到了需要停,所以只有找到的地方才有return。
  • 回溯的是nums里的元素,即使没有现场的设置恢复,但是仍然是顺序的套路,专门用框架写出来,并且把没用的代码注释掉,对比看下。
  • 不能使用重复的元素,所以在dfs往下走的时候需要+1

(2)递归法

class Solution:
	def canPartition_(self, nums):
    nums_sum=sum(nums)
    if nums_sum%2!=0:
        return False
    else:
        nums_sum//=2
    rec=[False]
    
    def search(nums,i,s=0):
        if i==len(nums) or s>nums_sum:
            return
        if nums[i]+s==nums_sum:
            rec[0]=True
            return
        else:
            search(nums,i+1,s)
            search(nums,i+1,s+nums[i])
    search(nums,0,0)
    return rec[0]
  • 这里递归非常的优秀,和最长子串异曲同工,要求在递归函数里的内容必须一样的,所以递归里都是要+当前的数字,在递归的两个分叉路口走不一样的方法。
  • 因为有错的时候要退出,所以对错都有return。

硬币找零2 lc 581

找到所有找零的方案。

class Solution:
    def change(self,amount, coins):
        def dfs(start_index, amount, coins,res):
            if amount == 0:
                res[0]+=1
                return
            if amount<0:
                return
            for i in range(start_index,len(coins)):
                dfs(i,amount-coins[i],coins,res)
        res = [0]
        start_index = 0
        dfs(start_index, amount, coins, res)
        return res[0]

1. 数字键盘组合 lc 17

在这里插入图片描述

Input:Digit string “23”
Output: [“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].

import copy
class Solution:
    def __init__(self):
        self.letterMap = [
            ' ',
            '',
            'abc',
            'def',
            'ghi',
            'jkl',
            'mno',
            'pqrs',
            'tuv',
            'wxyz'
        ]

    def flash_back(self,digits,index,res,path):
        if len(digits) == index:
            res.append(''.join(copy.deepcopy(path)))
            return
        char_ = digits[index]
        letters = self.letterMap[ord(char_)-ord('0')]
        for letter in letters:
            path.append(letter)
            self.flash_back(digits,index+1,res,path)
            path.pop()

    def letterCombinations(self, digits):
        res = []
        path = []
        if not digits:
            return res
        self.flash_back(digits,0,res,path)
        return res
s = Solution()
print(s.letterCombinations('23'))
  • 因为要把所有的可能性都列出来,所以是回溯。
  • 回溯的五个要点:当前的字符串、目标值、index、path为当前的解,res需要把path进行深度拷贝。不一定全部都要,比如本题不需要目标值,因为index到位了就结束。
  • 设置现场,递归,回复现场。本次因为不允许重复取所以index+1
  • 稍微复杂的是需要有解码的过程,按照电话号码直接建设list然后解码。
  • 判断条件是最外层被包装过的比如要遍历回溯的被编码成一个数字,但是for回溯的时候,是很对字符串,一定会有一个条件看这个字符串是否走到头。

2. IP 地址划分 lc 93

有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组

成,且不能含有前导 0),整数之间用 ‘.’ 分隔。

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是
“0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效的 IP 地址。

Given “25525511135”,
return [“255.255.11.135”, “255.255.111.35”].
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
输入:s = “101023”
输出:[“1.0.10.23”,“1.0.102.3”,“10.1.0.23”,“10.10.2.3”,“101.0.2.3”]

class Solution:
    def restoreIpAddresses(self,s):
        res = []
        path = []
        if not s or len(s)<4 or len(s)>12:
            return res
        self.dfs(s,4,0,res,path)
        return res


    def isnum(self,string):
        if 0<=int(string)<=255 and str(int(string))==string:
            return True
        else:
            return False


    def dfs(self,s,n,index,res,path):
        if n==0 and index == len(s):
            res.append('.'.join(copy.deepcopy(path)))
            return
        for i in range(index+1,len(s)+1):
            if self.isnum(s[index:i]):
                path.append(s[index:i])
                self.dfs(s,n-1,i,res,path)
                path.pop()
            else:
                break
  • 两个条件的回溯。
  • 问题解耦,需要遍历到字符串的结尾,需要满足四段,但是每一段是否是符合条件的数字组合。
  • 一个数字一个数字加上去不能判断是否是四段,想用这个框架就需要让数字形成一个坨即可。
  • 遍历字符串的方法,再循环内部的遍历+1,固定起始位置,让结尾往后推。
  • 首位数字不能为0的标准检查方法。

3. 在矩阵中寻找字符串 lc 79

给定一个二维网格和一个单词,找出该单词是否存在于网格中。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
For example,
Given board =
[
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]
]
word = “ABCCED”, -> returns true,
word = “SEE”, -> returns true,
word = “ABCB”, -> returns false.

棋盘终于和回溯见面了

class Solution:
    def __init__(self):
        self.dir = [(0, 1), (0, -1), (1, 0), (-1, 0)]

    def exist(self,board,word):
        if not word or not board:return False
        r = len(board)
        c = len(board[0])
        visited = [[False] * c for _ in range(r)]
        res=[False]
        for i in range(r):
            for j in range(c):
                if board[i][j] == word[0]:
                    self.dfs(board,word,i,j,0,visited,res)
                    #不管走通没走通,递归出来都要还原现场
                    visited[i][j] = False
        return res[0]

    def dfs(self,board,word,i,j,index,visited,res):
    #能进入递归说明可行,要设置该值
        visited[i][j] = True

        if index+1 == len(word):
            res[0] = True
            return

        for r,c in self.dir:
            nr = i+r
            nc = j+c
            if 0<=nr<len(board) and 0<=nc<len(board[0]) and visited[nr][nc]==False and index+1<len(word):
                if board[nr][nc]==word[index+1]:
                    self.dfs(board, word, nr, nc, index+1,visited,res)
                    if res[0]==True:
                        return
                    visited[nr][nc] = False
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页