Skip to content

Latest commit

 

History

History
1161 lines (963 loc) · 36.2 KB

回溯算法.md

File metadata and controls

1161 lines (963 loc) · 36.2 KB

39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。

说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。 
示例 1:

输入: candidates = [2,3,6,7], target = 7
所求解集为:
[
[7],
[2,2,3]
]
示例 2:

输入: candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

链接:https://leetcode-cn.com/problems/combination-sum

    class Solution:        
        def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
            candidates.sort()
            result = []
            
            def recursion( candidates: List[int], target: int, tmp_list: List[int]) :
                #终止条件
                if sum(tmp_list) == target:
                #注意这时候是要copy一个新的list  纠正 并不需要 可以直接result.append(tmp_list) 减少时间
                    result.append(tmp_list[:])
                    return 
                
                #广度遍历
                for i in candidates:
                    if sum(tmp_list)+ i >target:
                        break
                    #继续向下 深度搜索 
                    new = list(filter(lambda x :x >=i , candidates))
                    #使用这种方式 tem+[i]  不需要 先 append 再 pop  不断地尝试不同组合
                    recursion(new, target, tmp_list+[i])
                return 
            
            
            recursion(candidates, target, [])
            return result

40. 组合总和 II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。 
示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
  [1,2,2],
  [5]
]

链接:https://leetcode-cn.com/problems/combination-sum-ii

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        candidates.sort()
        result = []
        tmp_list = []
        
        def recursion( candidates: List[int], target: int, tmp: List[int]):
            if sum(tmp) == target:
                result.append(tmp[:])
                return 
            
            for i in range(len(candidates)):
                #剪枝处理
                num = candidates[i]
                if sum(tmp) + num > target:
                    break
                #用来去重的部分  就是跳过兄弟节点相同的部分
                if i > 0 and candidates[i] == candidates[i-1]:
                    continue
                new = candidates[i+1:]
                recursion(new, target, tmp+[num])
                
            return 
        
        recursion(candidates, target, tmp_list)
        return result
        

46. 全排列

给定一个没有重复数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

链接:https://leetcode-cn.com/problems/permutations

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        res = []
        length = len(nums)
        if len(nums) == 1:
            return [nums]
        
        def Traversal(tmp_nums:List[int],  cur_len: int, tmp: List[int]):
            if cur_len == length:
                res.append(tmp)
                return 
            for num in tmp_nums:
                new = tmp_nums[:]
                new.remove(num)
                Traversal(new, cur_len+1, tmp+[num])
            return 
        
        Traversal(nums, 0, [])
        return res                   
        

可以学习参考下这里面的模板 主要有used数组

class Solution:

    def permute(self, nums):
        if len(nums) == 0:
            return []

        used = [False] * len(nums)
        res = []
        self.__dfs(nums, 0, [], used, res)
        return res

    def __dfs(self, nums, index, pre, used, res):
        # 先写递归终止条件
        if index == len(nums):
            res.append(pre.copy())
            return

        for i in range(len(nums)):
            if not used[i]:
                # 如果没有用过,就用它
                used[i] = True
                pre.append(nums[i])

                # 在 dfs 前后,代码是对称的
                self.__dfs(nums, index + 1, pre, used, res)

                used[i] = False
                pre.pop()

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/two-sum/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

47. 全排列 II

给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

链接:https://leetcode-cn.com/problems/permutations-ii

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        res = []
        length = len(nums)
        lastUsed = nums[0]
        if len(nums) <= 1:
            return [nums]
        
        def Traversal(tmp_nums:List[int],  cur_len: int, tmp: List[int]):
            if cur_len == length:
                res.append(tmp[:])
                return 
            for i in range(len(tmp_nums)):
                if i != 0 and tmp_nums[i] == tmp_nums[i-1]:
                    continue
                Traversal(tmp_nums[:i]+tmp_nums[i+1:], cur_len+1, tmp+[tmp_nums[i]])
            return 
        
        Traversal(nums, 0, [])
        return res
    
    #思想是先对nums数组进行排列,然后在递归的过程中维护一个lastUsed变量,存储本次迭代的for循环中最近使用的数字,如果下一轮循环的数字是lastUsed,就说明重复了,就跳过本次循环
    
    #注意要比较的是 本次循环中 前后的数 不能相等 不是上下两级树的值不相等(是兄弟节点不同 不是父子节点)
    #注意 lastUsed要在for循环遍历外面 进行初始化
    
    #实际并不需要lastUsed  直接使用数组的元素即可 tmp_nums[i-1] == tmp_nums[i]

78. 子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
[3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

链接:https://leetcode-cn.com/problems/subsets

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        if len(nums) == 0:
            return [[]]
        nums.sort()
        res = []
        length = len(nums)
        
        #两种方法 一种是保存每一次的 有新的数则 在原来的基础上 每个list都加这个数
        """
        def recursion(num: int, cur_len :int):
            if cur_len == 1:
                res.append([])
                res.append([num])
            else:
                sub = res[:]
                for item in sub:
                    res.append(item+[num])
                
        
        for index, num in enumerate(nums):
            recursion(num, index+1)
        
        return res
        """  
        """     调用自身 直接遍历结果 增加新的元素
        if length == 1:
            return [[],[nums[0]]]
        else:
            sub = self.subsets(nums[:length-1])
            for i in sub:
                res.append(i+[nums[length-1]])
                res.append(i)
        
        
        #尝试使用回溯算法
        def recursion(tmp: List[int], index: int):
            res.append(tmp[:])
            if index == length:
                return
            for i in range(index, length):
                recursion(tmp+[nums[i]], i+1)
        recursion([],0)
        """
        
        #尝试自己的回溯方法
        def recursion(tmp: List[int], index: int, tmp_nums: List[int]):
            res.append(tmp[:])
            if index == length:
                return
            for i in range(len(tmp_nums)):
                recursion(tmp+[tmp_nums[i]], i+1, tmp_nums[i+1:])
        recursion([], 0, nums)
        return res
        

90. 子集 II

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]

链接:https://leetcode-cn.com/problems/subsets-ii

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        if len(nums) == 0:
            return [[]]
        nums.sort()
        res = []
        length = len(nums)
        """
        self.lastadd = 0 
        def recursion(num: int, cur_len :int):
            if cur_len == 1:
                res.append([])
                res.append([num])
                self.lastadd = 1
            else:
                sub = res[:]
                for item in sub:
                    res.append(item+[num])
                self.lastadd = len(res) - len(sub)
                
        def recursion2(num: int, cur_len :int):
            sub = res[len(res)-self.lastadd:]
            for item in sub:
                    res.append(item+[num])
            lastadd = len(sub)
        
        for index, num in enumerate(nums):
            if index > 0 and num == nums[index-1]:
                recursion2(num, index+1)
            #不同的数进行的是 原来的操作
            else :
                recursion(num, index+1)
        
        
        
        #尝试使用回溯算法
        def recursion(tmp: List[int], index: int, tmp_nums: List[int]):
            res.append(tmp[:])
            if index == length:
                return
            for i in range(len(tmp_nums)):
                if i > 0 and tmp_nums[i] == tmp_nums[i-1]:
                    continue 
                recursion(tmp+[tmp_nums[i]], i+1, tmp_nums[i+1:])
        recursion([], 0, nums)
        """
        #使用回溯另外的表示方法  不需要另外保存 tmp_nums 直接用下标来表示
        
        def recursion(tmp: List[int], index: int):
            res.append(tmp[:])
            if index == length:
                return
            for i in range(index, length):
                #注意是 i> index 不是i》0
                if i > index and nums[i] == nums[i-1]:
                    continue 
                recursion(tmp+[nums[i]], i+1)
        recursion([], 0)
        
        return res
            

17.电话号码的字母组合

简单的回溯应用

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        # 注意边界条件的判断考虑
        if not digits:
            return []
        dic = {
            "2": ["a", "b", "c"],
            "3": ["d", "e", "f"],
            "4": ["g", "h", "i"],
            "5": ["j", "k", "l"],
            "6": ["m", "n", "o"],
            "7": ["p", "q", "r", "s"],
            "8": ["t", "u", "v"],
            "9": ["w", "x", "y", "z"]
        }
        
        n = len(digits)
        res = []
        
        def recursion(tmp_res: str, deep: int):
            if deep == n:
                res.append(tmp_res)
                return
            #应该遍历的是 digits[deep] 对应的 数字的list 
            for tmp in dic[digits[deep]]:
                recursion(tmp_res+ tmp, deep+1)
        recursion("", 0)
        return res
        #如何分析回溯法的时间复杂度

22. 括号生成

自己先写的回溯法

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        # 初步感觉 可以用回溯 可以用出栈的办法 
        
        if not n :
            return []
        res = []
        tmp = ""
        brackets = ['(', ')']
        left_num  = 0 
        right_num = 0
        def recursion(tmp_res: str, left_num: int, right_num: int, cur_len: int):
            #print(tmp_res, left_num, right_num, cur_len)
            if cur_len == 2*n:
                res.append(tmp_res)
                return 
            else:
                for i in range(len(brackets)):
                    if brackets[i] == '(':
                        left_num += 1
                    else:
                        right_num += 1
                    if left_num < right_num or left_num > n  or right_num > n:
                        #剪枝 是跳出循环吗
                        if brackets[i] == '(':
                            left_num -= 1
                        else:
                            right_num -= 1
                        continue
                    recursion(tmp_res+ brackets[i], left_num, right_num, cur_len+1)
                    if brackets[i] == '(':
                        left_num -= 1
                    else:
                        right_num -= 1
                    
        recursion("", 0, 0, 0)
        return res

看着更简洁的方式

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        # 初步感觉 可以用回溯 可以用出栈的办法 
        
        if not n :
            return []
        res = []
        tmp = ""
        brackets = ['(', ')']
        left_num  = 0 
        right_num = 0
        def recursion(tmp_res: str, left_num: int, right_num: int):
            if left_num == n and right_num == n:
                res.append(tmp_res)
                return 
            if left_num<n:
                recursion(tmp_res +'(', left_num+1, right_num)
            if right_num <n and  left_num > right_num:
                recursion(tmp_res +')', left_num, right_num+1)

        recursion("", 0, 0)
        return res

51 N皇后问题

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

一开始的解法 没有问题 只是时间太长 做了很多的重复工作 想办法优化

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        res = []
        cur_depth = 0
        
        def recursion(cur, tmp_res):
            #print(cur, tmp_res)
            if cur == n:
                #print(tmp_res)
                res.append(tmp_res)
                return 
            
            tmp_list = ['.' for i in range(n)] 
            for i in range(n):
                #需要剪枝 在进入之前  还是进入之后
                tmp_list[i] = 'Q' 
                tmp_str = ''.join(tmp_list)
                #tmp_res.append(tmp_str)
                if judge(tmp_res+[tmp_str]) :
                    #进入下一层 
                    recursion(cur+1, tmp_res+[tmp_str])
                #恢复现场
                tmp_list[i] = '.'
                
        def judge(tmp):
            #print(tmp)
            info = []
            dep = len(tmp)
            for i in range(dep):
                col = tmp[i].index('Q')
                if len(info):
                    for tmpinfo in info:
                        if abs(i-tmpinfo[0]) == abs(col-tmpinfo[1]) or col ==tmpinfo[1] :
                            return False
                tmp_info = [i, col]
                info.append(tmp_info)
                
            return True
        
        recursion(0, [])
        return res

queens = []#第i个位置表示第i行,queens[i]=j表示(i,j)有棋子。 直接在函数中进行判断,不需要每次都分析res的过去 保存

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        res = []
        
        #queens = []#第i个位置表示第i行,queens[i]=j表示(i,j)有棋子。
        def recursion(cur, queens):
            if cur == n:
                res.append(queens[:])
                return 

            for col in range(n):
                flag = True
                #需要剪枝 在进入之前  还是进入之后
                queens.append(col)
                #出现问题 是全部for循环 肯定会有置为flag false的
                if cur >= 1:
                    for j in range(cur):
                        if queens[j] == col or cur - j ==abs(col - queens[j]):
                            flag = False
                            break
                if flag:
                    recursion(cur + 1,queens)
                queens.pop()
        
        recursion(0, [])
        #print(res)
        t1 = []
        for tmp in res:
            t2 = []
            for k in tmp:
                tmp_list = ['.']*n
                tmp_list[k] = 'Q'
                tmp_str = ''.join(tmp_list)
                t2.append(tmp_str)
            t1.append(t2) 
        #print(t1)
        return t1

60. 第k个排列

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
示例 1:

输入: n = 3, k = 3
输出: "213"
示例 2:

输入: n = 4, k = 9
输出: "2314"

自己一开始还是用的全排列的情况 然后直接找第k个 并且有剪枝 但是TLE 但记录自己的回溯法

class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        res = ""
        lis = [i for i in range(1,n+1)]
        count = 0
        flag = False
        def recursion(tmp_res, tmp):
            nonlocal res
            nonlocal count
            nonlocal flag
            if flag:
                return 
            if len(tmp_res) == n:
                count += 1
                if count == k:
                    #print(tmp_res)
                    res = ''.join([str(i) for i in tmp_res])
                    flag = True
                return 
            
            for i in range(len(tmp)):
                recursion(tmp_res+[tmp[i]], tmp[:i]+tmp[i+1:])
        
        recursion([], lis)
        return res
  1. 增加了剪枝操作 比较k和子树的大小 跳过很多子树
  2. used数组来保存 而不是原来的 对数组切片 传参
class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        res = ""
        lis = [i for i in range(1,n+1)]
        factorial = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
        used = [False for _ in range(n)]
        flag = False
        
        def recursion(tmp_res, k, used):
            nonlocal flag
            nonlocal res
            ps = factorial[n-1-len(tmp_res)]
            if len(tmp_res) == n and flag == False:
                #print(tmp_res)
                res = ''.join([str(i) for i in tmp_res])
                flag = True
                return 
            
            for i in range(n):
                if used[i]:
                    continue
                if k > ps:
                    k -= ps
                    continue
                used[i] = True
                recursion(tmp_res+[i+1], k, used)
        
        recursion([], k, used)
        #print(res)
        return res

77. 组合

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

常规的解法,不过很有效的就是增加了一个剪枝 对i的循环范围的判断

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        res = []
        lis = [i+1 for i in range(n)]
        
        def recursion(cur_len, tmp_res, start):
            if cur_len == k:
                res.append(tmp_res[:])
                return 
            #
            for i in range(start,  n - (k - cur_len) + 1):
                    tmp_res.append(lis[i])
                    recursion(cur_len+1, tmp_res, i + 1)
                    tmp_res.pop()
        
        recursion(0, [], 0)
        return res
        
        

79. 单词搜索

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

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例:

board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]

给定 word = "ABCCED", 返回 true.
给定 word = "SEE", 返回 true.
给定 word = "ABCB", 返回 false.

其实是迷宫问题+ 二维平面的遍历搜索

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        #二维平面搜索问题
        m = len(board)
        if m == 0:
            return False
        n = len(board[0])
        length = len(word)
        marked = 
        #遍历二维平面的四个方向
        directions = [(0, -1), (1, 0), (0, 1), (-1, 0)]
        
        def judge(x, y, cur_len):
            #print(x, y, cur_len)
            #终止判断条件
            if cur_len == length:
                #print("test") 
                return True
            
            for item in directions:
                new_x = x + item[0]                                     
                new_y = y + item[1]
                if 0 <= new_x < m and 0 <= new_y <n and not marked[new_x][new_y]:
                    #上面是前提条件的判断  保证下标不越界 保证不出现重复使用(走过的要有标记)
                    # 置位的位置 是直接置位还是 在判断相等之后再
                    if board[new_x][new_y] == word[cur_len]:
                        marked[new_x][new_y] = 1
                        # 注意和原先的不一样 这个在每次成功调用之后 一层一层的往回返回True  才能最终收到True的结果 
                        if judge(new_x, new_y, cur_len+1):
                            return True
                        marked[new_x][new_y] = 0
            return False
            
        #出现问题 结果返回不对 没有返回值
        for i in range(m):
            for j in range(n):
                if board[i][j] == word[0]:
                    #调用判断函数
                    # 注意在首位置上也要进行回溯 先标记后修改
                    marked[i][j] = 1
                    if judge(i, j, 1):
                        return True
                    marked[i][j] = 0
        return False
        
        

更好的答案的方法:

class Solution:

    #遍历二维平面的四个方向
    directions = [(0, -1), (1, 0), (0, 1), (-1, 0)]

    def exist(self, board: List[List[str]], word: str) -> bool:
        #二维平面搜索问题
        m = len(board)
        if m == 0:
            return False
        n = len(board[0])
        length = len(word)
        marked = [[False for _ in range(n)] for _ in range(m)]  
        #出现问题 结果返回不对 没有返回值
        for i in range(m):
            for j in range(n):
                if self.__search_word(board, word, 0, i, j, marked, m, n):
                    return True
        return False
        
    def __search_word(self, board, word, index, start_x, start_y, marked, m, n):
        # 终止条件
        if index == len(word) -1:
            return board[start_x][start_y] == word[index]
        
        # print(start_x, start_y)
        # 当前匹配了 继续搜索
        if board[start_x][start_y] == word[index]:
            # 当前元素成功 先置为正确
            marked[start_x][start_y] = True

            for direction in self.directions:
                new_x = start_x + direction[0]
                new_y = start_y + direction[1]
                if 0 <= new_x < m and 0 <= new_y < n and not marked[new_x][new_y] and self.__search_word(board, word,index + 1,
new_x, new_y,marked, m, n):
                    return True
			# 当前元素的后续没有解 恢复现场 
            marked[start_x][start_y] = False

        return False

93. 复原IP地址

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

示例:

输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/restore-ip-addresses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

细节内容比较多 注意看自己后面的出错情况

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        res = []
        
        def recursion(tmp_res, tmp):
            #print(tmp_res, tmp)
            if len(tmp_res) == 4:
                if not tmp:
                    res.append('.'.join(tmp_res))
                return 
            elif not tmp:
                return 
            lastused = -1
            for i in range(1, 4):
                #分别取1, 2, 3位数 做判断并加入 开始回溯
                if i <= len(tmp): 
                    tmp_num = int(tmp[:i])
                    if tmp_num > 255 :
                        continue
                    elif tmp_num == lastused:
                        continue
                    elif i> 1 and  tmp[0] == '0':
                        continue
                    lastused = tmp_num
                    recursion(tmp_res+[tmp[:i]], tmp[i:])
                    
        recursion([], s)
        return res
    
        #几个出现的错误 1. 超出len 出现len为5 的情况 没有加好限定  2. 传的是list  注意tmp[:i] 切片的结果也是list  
        # 3. 结果出现重复-去重? 使用lastused来维护 注意这是在一层while循环内部来比较 不需要考虑其他进入下一层的事情 是在比较兄弟节点是否相同 
        # 出现1111 int  原因是tmp为空 还在遍历list 增加一开始的限制 
        #4, 用的是 先append 再pop 则结果需要[:] 复制链表
        # 出现 0000 错误 设置的lastused是 0 
        # "010010"  每段ip的要求不仅是0-255,同时每段不得有前置零,且不得删除任何一个数,即ip地址总长度不能变 #除去0开头,且长度大于1情况

127. 单词接龙

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:

每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。

输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

输出: 5

解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
    返回它的长度 5。

由最短序列想到应该是 BFS 然后这道题的关键在于 使用双端BFS来解决超时问题

最终修改版本 主要改动是变成了双向BFS 选择了分支小的那一侧来进行BFS遍历 即
class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        if endWord not in wordList:
            return 0 
        wordList = set(wordList)
        str_len = len(endWord)
        
        head = {beginWord}
        tail = {endWord}
        
        tmp = list('abcdefghijklmnopqrstuvwxyz')
        
        cur_len = 1
        
        while head:
            #关键点 在这 就是交换了首位逼近 通过首尾来互相逼近
            if len(head) > len(tail):
                head, tail = tail, head
            
            next = set()
            for cur in head :
                for i in range(str_len):
                    for j in tmp:
                    #用这种方式来直接生成下一个next的内容
                        word = cur[:i] + j + cur[i+1:]
                        if word in tail:
                            return cur_len+1
                        if word in wordList:
                            next.add(word)
                            wordList.remove(word)
            
            head = next
            cur_len += 1
        return  0 

130. 被围绕的区域(反向DFS 或者并查集)

关于并查集 : https://blog.csdn.net/liujian20150808/article/details/50848646#commentBox 给定一个二维的矩阵,包含 'X' 和 'O'(字母 O)。

找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。

主要思想是反向思考: 从边界上的O点开始去试探,深度搜索,把能遍历到的O点置为# 最后再修改#-》O 详见代码

class Solution:
    def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        if not board:
            return 
        m = len(board)
        n = len(board[0]) 
        if m <= 2 or n <= 2 :
            return 
        
        def search(x, y):
            directions = [[-1, 0], [0, 1], [1, 0], [0,-1]]
            for item in directions:
                new_x = x + item[0]
                new_y = y + item[1]      
                if 0 <= new_x <= m-1 and 0 <= new_y <= n-1:
                    if board[new_x][new_y] == 'O':
                        board[new_x][new_y] = '#'
                        search(new_x, new_y)
        
        #由边界开始遍历
        for j in range(0, n-1):
            if board[0][j] == 'O':
                board[0][j] = '#'
                #开始进行搜索 置位
                search(0, j)
        for i in range(0, m-1):
            if board[i][n-1] == 'O':
                board[i][n-1] = '#'
                search(i, n-1)
        for j in range(1,n)[::-1]:
            if board[m-1][j] == 'O':
                board[m-1][j] = '#'
                search(m-1, j)
        for i in range(1, m)[::-1]:
            if board[i][0] == 'O':
                board[i][0] = '#'
                search(i, 0)
        #最后的修改 
        for i in range(0, m):
            for j in range(0, n):
                if board[i][j] == 'O':
                    board[i][j] = 'X'
                elif board[i][j] == '#':
                    board[i][j] = 'O'
        return 

131. 分割回文串

回溯的思路 写起来更加方便快捷 python3 用回溯递归的方法去试探每一种可能性 对于一个字符串s,有len(s)种方法把它分成左右两个部分(分割方法看代码),假如左侧的不是回文,则舍弃这次尝试;假如左侧的是回文串,则把右侧的进行递归的分割,并返回右侧的分割的所有情况

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        if not s:
            return None
        l = len(s)
        if  l == 1:
            return [[s]]
        res = []
        
        def recursion(s, tmp):
            if not s:
                res.append(tmp)
                return 
            
            for i in range(1, len(s)+1):
                left = s[:i]
                right = s[i:]
                if left == left[::-1]:
                    recursion(right, tmp+[s[:i]])
        
        recursion(s, [])
        return res

一开始的做法 有动态规划 以及找规律的意思

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        if not s:
            return None
        l = len(s)
        if  l == 1:
            return [[s]]
        if l == 2:
            if s[0] == s[1]:
                return [[s], [s[0], s[1]]]
            else:
                return [[s[0], s[1]]]
        res = [[] for _ in range(l)]
        for i in range(l):
            if i == 0:
                res[i] = [[s[0]]]
            #每次递增的有在原来基础上直接相加的 还有自己尝试是否相等的
            else:
                #去做判断 尝试和前面的合并看是否是 
                for j in range(i)[::-1]:
                    if s[j:i+1] == s[j:i+1][::-1]:
                        #print(s[j:i+1])
                        #需要增加元素
                        #print(res[j-1])
                        if j == 0:
                            res[i].append([s[j:i+1]])
                        else:
                            for tmp in res[j-1]:
                                tmp1 = tmp + [s[j:i+1]]
                                res[i] += [tmp1]
                
                res[i] += [item+ [s[i]] for item in res[i-1]]
                #print(res[i])
        return res[l-1]

37.解数独

思路很直接: 就是对每一个空着的格子穷举 1 到 9 找到一个合法的数字,则继续穷举下一个空格子。

要注意的就是: 只需要一个解,如何返回这个解 即对false 和 true的使用 何时返回 此时每一个其实都return了

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        # 算法思路十分单纯,就是对每一个空着的格子穷举 1 到 9 找到一个合法的数字,则继续穷举下一个空格子。
        def backtrack(board, i, j):
            m, n = 9, 9 
            # 遍历每一个格子 遇到最后一列则加一
            if j == 9:
                return backtrack(board, i+1, 0)
                 

            # 遇到最后一行0-8 是正常的 即遍历完成
            if i == 9:
                #print(board)
                return True

            # 正常格子
            if board[i][j] != '.':
                return backtrack(board, i, j+1)
                 
                 
            
            # 遇到 空格开始 遍历
            for k in range(1, 10):
                tmp = str(k)
                if isvalid(board, i, j, tmp):
                    board[i][j] = tmp
                    if backtrack(board, i, j+1):
                        return True
                    board[i][j] = '.'
            # 不要忘记 每一个都会返回 
            return False
            

        # def isvalid(board, i, j, k):
        #     now = k
        #     # 这是一行的判断
        #     if board[i].count(now) >= 1:
        #         return False
        #     # 列的判断
        #     for x in range(9):
        #         if board[x][j] == now :
        #             return False

        #     # 一个方框的判断 
        #     tmp_i, tmp_j = i//3, j//3
        #     # print(tmp_i, tmp_j)
        #     tmp_list = []
        #     for x in range(tmp_i*3, tmp_i*3+3):
        #         for y in range(tmp_j*3, tmp_j*3+3):
        #             tmp_list.append(board[x][y])
        #     if now in tmp_list:
        #         return False
            
        #     return True

        def isvalid(board, i, j, x):
            for t in range(9):
                # 这种队友一行一列的处理 值得学习
                if board[t][j] == x: return False
                if board[i][t] == x: return False
                if board[i//3*3+ t//3][j//3*3+ t%3] == x: return False
            return True

        # def isvalid(board:List[List[str]],i:int,j:int,x:str) -> bool:
        #     for t in range(9):
        #         if board[t][j] == x:return False
        #         if board[i][t] == x:return False
        #         if board[i//3* 3 + t//3][j//3*3 + t%3] == x: return False
        #     return True


        #print(board)
        backtrack(board, 0, 0)