代码随想录算法训练营第十六天 | 回溯系列3与回溯系列4

90 子集 II

未看解答自己编写的青春版

这题重点在于去重,去重过程直接套用前面讲的used数组即可,重点在于:要先排序!!!不要忘记!!!

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        if nums == []:
            return []

        self.res = []
        path = []
        idx = 0
        nums.sort()
        n = len(nums)
        used = [False]*n

        self.digui(nums,idx,path,used)
        self.res.append([])

        return self.res


    def digui(self,nums,idx,path,used) :

        for i in range(idx,len(nums)):
            if i > 0 and nums[i]==nums[i-1] and not used[i-1] :
                continue
            path.append(nums[i])
            used[i] = True
            self.res.append(path.copy())
            self.digui(nums,i+1,path,used)
            used[i]=False
            path.pop()

另一种去重

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        if nums == []:
            return []

        self.res = []
        path = []
        idx = 0
        nums.sort()
        n = len(nums)

        self.digui(nums,idx,path)
        self.res.append([])

        return self.res


    def digui(self,nums,idx,path) :

        for i in range(idx,len(nums)):
            if i > idx and nums[i]==nums[i-1] :
                continue
            path.append(nums[i])
            self.res.append(path.copy())
            self.digui(nums,i+1,path)
            path.pop()

重点

卡哥说,使用used数组的去重方法更为通用,当遇到排列问题中的去重,就只能使用used数组方法了。

代码随想录的代码

回溯 利用used数组去重

class Solution:
    def subsetsWithDup(self, nums):
        result = []
        path = []
        used = [False] * len(nums)
        nums.sort()  # 去重需要排序
        self.backtracking(nums, 0, used, path, result)
        return result

    def backtracking(self, nums, startIndex, used, path, result):
        result.append(path[:])  # 收集子集
        for i in range(startIndex, len(nums)):
            # used[i - 1] == True,说明同一树枝 nums[i - 1] 使用过
            # used[i - 1] == False,说明同一树层 nums[i - 1] 使用过
            # 而我们要对同一树层使用过的元素进行跳过
            if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]:
                continue
            path.append(nums[i])
            used[i] = True
            self.backtracking(nums, i + 1, used, path, result)
            used[i] = False
            path.pop()

回溯 利用递归的时候下一个startIndex是i+1而不是0去重

class Solution:
    def subsetsWithDup(self, nums):
        result = []
        path = []
        nums.sort()  # 去重需要排序
        self.backtracking(nums, 0, path, result)
        return result

    def backtracking(self, nums, startIndex, path, result):
        result.append(path[:])  # 收集子集
        for i in range(startIndex, len(nums)):
            # 而我们要对同一树层使用过的元素进行跳过
            if i > startIndex and nums[i] == nums[i - 1]:
                continue
            path.append(nums[i])
            self.backtracking(nums, i + 1, path, result)
            path.pop()

利用set去重

class Solution:
    def subsetsWithDup(self, nums):
        result = []
        path = []
        nums.sort()  # 去重需要排序
        self.backtracking(nums, 0, path, result)
        return result

    def backtracking(self, nums, startIndex, path, result):
        result.append(path[:])  # 收集子集
        uset = set()
        for i in range(startIndex, len(nums)):
            if nums[i] in uset:
                continue
            uset.add(nums[i])
            path.append(nums[i])
            self.backtracking(nums, i + 1, path, result)
            path.pop()

我的代码(当天晚上理解后自己编写)

利用 used 数组进行去重

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        self.res = []
        path = []
        used = [False]*len(nums)
        nums.sort()
        idx = 0

        self.backtracking(nums,idx,path,used)
        self.res.append([])

        return self.res

    def backtracking(self,nums,idx,path,used):
        for i in range(idx,len(nums)):
            if i > 0 and nums[i-1]==nums[i] and used[i-1]==False :
                continue
            path.append(nums[i])
            used[i]=True
            self.res.append(path.copy())
            self.backtracking(nums,i+1,path,used)
            used[i]=False
            path.pop()
        return

利用下面“递增子序列”中介绍的去重逻辑,重新编写的一版代码,因为发现,这两个用used去重,所要实现的目的均为树层去重,那么他们是不是通用的呢?

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        self.res = []
        path = []
       
        nums.sort()
        idx = 0

        self.backtracking(nums,idx,path)
        self.res.append([])

        return self.res

    def backtracking(self,nums,idx,path):
        used = [False]*21
        for i in range(idx,len(nums)):
            if i > 0 and nums[i-1]==nums[i] and used[nums[i]+10]==True :
                continue
            path.append(nums[i])
            used[nums[i]+10]=True
            self.res.append(path.copy())
            self.backtracking(nums,i+1,path)
            
            path.pop()
        return

下面又精简了一下判断条件,也是OK的

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        self.res = []
        path = []
       
        nums.sort()
        idx = 0

        self.backtracking(nums,idx,path)
        self.res.append([])

        return self.res

    def backtracking(self,nums,idx,path):
        used = [False]*21
        for i in range(idx,len(nums)):
            if used[nums[i]+10]==True :
                continue
            path.append(nums[i])
            used[nums[i]+10]=True
            self.res.append(path.copy())
            self.backtracking(nums,i+1,path)
            
            path.pop()
        return

感悟

如果能够对数组进行排序的题,比如本题,肯定就用全局used数组最好,也好理解。当然,递归新定义used也是可以的。

如果是不能排序的题,比如下面这道,递增子序列,的题,是不能用全局used数组的,只能递归新定义used。

递归新定义used,用哈希表代替set的前提是,题目说明了数组元素有界。

这两种去重方法均为树层去重,我认为在功能上没什么区别,但是从理论上分析可知,递归新定义used数组,空间和时间开销都是大的。

491 递增子序列

不会,想不到去重逻辑,只能想到用set去重,这种方法肯定是不推荐的。还有就是遍历used数组,但是感觉这也有点复杂吧。

未看解答自己编写的青春版

重点

没想到,代码随想录的解法竟然也是基于set。

不过其给出的,使用哈希表的做法还是值得学习的。

代码随想录的代码

回溯,利用set去重

class Solution:
    def findSubsequences(self, nums):
        result = []
        path = []
        self.backtracking(nums, 0, path, result)
        return result
    
    def backtracking(self, nums, startIndex, path, result):
        if len(path) > 1:
            result.append(path[:])  # 注意要使用切片将当前路径的副本加入结果集
            # 注意这里不要加return,要取树上的节点
        
        uset = set()  # 使用集合对本层元素进行去重
        for i in range(startIndex, len(nums)):
            if (path and nums[i] < path[-1]) or nums[i] in uset:
                continue
            
            uset.add(nums[i])  # 记录这个元素在本层用过了,本层后面不能再用了
            path.append(nums[i])
            self.backtracking(nums, i + 1, path, result)
            path.pop()

回溯 利用哈希表去重

class Solution:
    def findSubsequences(self, nums):
        result = []
        path = []
        self.backtracking(nums, 0, path, result)
        return result

    def backtracking(self, nums, startIndex, path, result):
        if len(path) > 1:
            result.append(path[:])  # 注意要使用切片将当前路径的副本加入结果集
        
        used = [0] * 201  # 使用数组来进行去重操作,题目说数值范围[-100, 100]
        for i in range(startIndex, len(nums)):
            if (path and nums[i] < path[-1]) or used[nums[i] + 100] == 1:
                continue  # 如果当前元素小于上一个元素,或者已经使用过当前元素,则跳过当前元素
            
            used[nums[i] + 100] = 1  # 标记当前元素已经使用过
            path.append(nums[i])  # 将当前元素加入当前递增子序列
            self.backtracking(nums, i + 1, path, result)
            path.pop()

我的代码(当天晚上理解后自己编写)

本题要点在于去重的逻辑,和以往遇到的题不同,不管是用set去重还是哈希表去重,都是在每层递归中新构造去重数组。

本题的去重逻辑:同一层父节点下的同层上使用过的元素就不能再使用了。

本质上还是树层去重。

class Solution:
    def findSubsequences(self, nums):
        self.res = []
        path = []
        idx = 0
        self.backtracking(nums,idx,path)

        return self.res

    def backtracking(self,nums,idx,path):
        if len(path)>1 :
            self.res.append(path.copy())
            
        used = [0]*201
        for i in range(idx,len(nums)):

            if (path and nums[i] < path[-1]) or used[nums[i]+100]==1 :

                continue
            used[nums[i]+100]=1
            path.append(nums[i])
            self.backtracking(nums,i+1,path)
            path.pop()

        return

46 全排列

未看解答自己编写的青春版

现学现卖,哈希表去重。因为注意到了,所有数值只在【-10,10】。

但是这份代码写的不好,used数组给大了,不需要用哈希表。

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        if nums == []:
            return []

        if len(nums)==1:
            return [[nums[0]]]

        self.res = []
        path = []
        idx = 0
        used = [0]*21
        self.digui(nums,path,used)

        return self.res
    def digui(self,nums,path,used):
        if len(path)==len(nums) :
            self.res.append(path.copy())
            return

        for i in range(0,len(nums)):
            if used[i+10] != 0 :
                continue
            path.append(nums[i])
            used[i+10] = 1
            self.digui(nums,path,used)
            used[i+10]=0
            path.pop()
        return

重点

此题和上一题,递增子序列,还是有差别的,此题的used数组是贯穿递归始终的,而上一题的used是每次递归,新申请的。

代码随想录的代码

class Solution:
    def permute(self, nums):
        result = []
        self.backtracking(nums, [], [False] * len(nums), result)
        return result

    def backtracking(self, nums, path, used, result):
        if len(path) == len(nums):
            result.append(path[:])
            return
        for i in range(len(nums)):
            if used[i]:
                continue
            used[i] = True
            path.append(nums[i])
            self.backtracking(nums, path, used, result)
            path.pop()
            used[i] = False

对比此题和上一题的去重逻辑,我悟了

递增子序列中,所引入的used数组,只在单个树层,不进入树枝,并且由于其有重复元素,所给nums数组中的index,无法和数值进行对应,所以要用哈希表来实现。

而本题中,used数组是贯穿始终的,是要进入树层和树枝的,并且,由于已知数组nums无重复元素,所以不需要哈希表,申请一个和nums大小相同的数组即可。

另外,本题的used数组的处理,这里的used数组不叫去重,因为求排列不涉及去重,used数组是为了标记,哪些元素已经被使用过。

之前在组合问题中,是使用idx来标记哪些元素被使用过,不会出现重复结果,但是在排列问题中,idx始终为0,或者说没有idx,这时候就需要used数组了。

我的代码(当天晚上理解后自己编写)

排列问题中,used数组是用来记录哪些元素已经被pick过。

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        self.res = []
        path = []
        used = [False] * len(nums)
        self.backtracking(nums, path, used)
        return self.res

    def backtracking(self, nums, path, used):
        if len(path) == len(nums):
            self.res.append(path.copy())
            return
        for i in range(len(nums)):
            if used[i]:
                continue
            used[i] = True
            path.append(nums[i])
            self.backtracking(nums, path, used)
            path.pop()
            used[i] = False

47 全排列 II

未看解答自己编写的青春版

两个去重一起来,我们都是好朋友。

但是又忘记去重前先排序了!!!

不知道写的好不好,看看代码随想录的解答。

和代码随想录的解答基本一致。

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        if nums == []:
            return []

        if len(nums)==1:
            return [[nums[0]]]

        self.res = []
        nums.sort()
        path = []
        idx = 0
        used = [0]*len(nums)
        self.digui(nums,path,used)

        return self.res

    def digui(self,nums,path,used):
        if len(path)==len(nums) :
            self.res.append(path.copy())
            return

        for i in range(0,len(nums)):
            if used[i] != 0 :
                continue
            if i > 0 and nums[i]==nums[i-1] and used[i-1]==0 :
                continue
            path.append(nums[i])
            used[i] = 1
            self.digui(nums,path,used)
            used[i]=0
            path.pop()
        return

去重前先排序,去重前先排序,去重前先排序

重要的事说三遍。

重点

本题有意思的地方在于,在去重判断中:

used[i-1]==0 和 used[i-1]==1 都是可以的

根据代码随想录的讲解,前者为树层去重,后者为树枝去重,具体可参考代码随想录的讲解文章。
全排列 II

但是我觉得这和之前一贯的写法产生出入,而且也没有太理解这种奇怪的情况,所以目前还是按照一贯的做法,就用 : used[i-1]==0 ,保持和之前学的去重方法一致。

代码随想录的代码

注意条件判断那里,和我自己的在写法上存在不同,但我认为,还是我写的,两个 if 判断更好理解一些,更能体现出,此题就是前面某两题的结合。

class Solution:
    def permuteUnique(self, nums):
        nums.sort()  # 排序
        result = []
        self.backtracking(nums, [], [False] * len(nums), result)
        return result

    def backtracking(self, nums, path, used, result):
        if len(path) == len(nums):
            result.append(path[:])
            return
        for i in range(len(nums)):
        # 这里注意,我自己写的代码是两个if判断,他这里合成一个了,所以最后多了一个or
            if (i > 0 and nums[i] == nums[i - 1] and not used[i - 1]) or used[i]:
                continue
            used[i] = True
            path.append(nums[i])
            self.backtracking(nums, path, used, result)
            path.pop()
            used[i] = False

我的代码(当天晚上理解后自己编写)

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        if nums == []:
            return []

        if len(nums)==1:
            return [[nums[0]]]

        self.res = []
        nums.sort()
        path = []
        idx = 0
        used = [0]*len(nums)
        self.digui(nums,path,used)

        return self.res

    def digui(self,nums,path,used):
        if len(path)==len(nums) :
            self.res.append(path.copy())
            return

        for i in range(0,len(nums)):
        # 记录哪些元素被选择过,这是排列问题的特性
            if used[i] != 0 :
                continue
        # 去重逻辑
            if i > 0 and nums[i]==nums[i-1] and used[i-1]==0 :
                continue
            path.append(nums[i])
            used[i] = 1
            self.digui(nums,path,used)
            used[i]=0
            path.pop()
        return

回溯系列3总结

代码随想录的总结中,对各个大类的问题的时间空间复杂度分析,分析的很好。
回溯系列3总结

回溯算法去重问题的另一种写法

重点

这篇文章主要讲了使用set来去重,里面涉及的知识点以及代码编写的易错点,但是set去重本身就慢,而且对于目前的我来说不好理解,觉得used数组的方法很好用,这里一刷就不去细看本文了。

回溯算法去重问题的另一种写法

332 重新安排行程

根本没思路啊,一点思路都没有,也不知道怎么判断行程合不合理。

未看解答自己编写的青春版

重点

这道题没有视频,只有文字讲解版。
重新安排行程

真难,也就看个一知半解。

代码随想录的代码

回溯 使用used数组

class Solution:
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:
        tickets.sort() # 先排序,这样一旦找到第一个可行路径,一定是字母排序最小的
        used = [0] * len(tickets)
        path = ['JFK']
        results = []
        self.backtracking(tickets, used, path, 'JFK', results)
        return results[0]
    
    def backtracking(self, tickets, used, path, cur, results):
        if len(path) == len(tickets) + 1:  # 终止条件:路径长度等于机票数量+1
            results.append(path[:])  # 将当前路径添加到结果列表
            return True
        
        for i, ticket in enumerate(tickets):  # 遍历机票列表
            if ticket[0] == cur and used[i] == 0:  # 找到起始机场为cur且未使用过的机票
                used[i] = 1  # 标记该机票为已使用
                path.append(ticket[1])  # 将到达机场添加到路径中
                state = self.backtracking(tickets, used, path, ticket[1], results)  # 递归搜索
                path.pop()  # 回溯,移除最后添加的到达机场
                used[i] = 0  # 标记该机票为未使用
                if state:
                    return True  # 只要找到一个可行路径就返回,不继续搜索

回溯 使用字典

from collections import defaultdict

class Solution:
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:
        targets = defaultdict(list)  # 构建机场字典
        for ticket in tickets:
            targets[ticket[0]].append(ticket[1])
        for airport in targets:
            targets[airport].sort()  # 对目的地列表进行排序

        path = ["JFK"]  # 起始机场为"JFK"
        self.backtracking(targets, path, len(tickets))
        return path

    def backtracking(self, targets, path, ticketNum):
        if len(path) == ticketNum + 1:
            return True  # 找到有效行程

        airport = path[-1]  # 当前机场
        destinations = targets[airport]  # 当前机场可以到达的目的地列表
        for i, dest in enumerate(destinations):
            targets[airport].pop(i)  # 标记已使用的机票
            path.append(dest)  # 添加目的地到路径
            if self.backtracking(targets, path, ticketNum):
                return True  # 找到有效行程
            targets[airport].insert(i, dest)  # 回溯,恢复机票
            path.pop()  # 移除目的地
        return False  # 没有找到有效行程

回溯 使用字典(逆序)

from collections import defaultdict

class Solution:
    def findItinerary(self, tickets):
        targets = defaultdict(list)  # 创建默认字典,用于存储机场映射关系
        for ticket in tickets:
            targets[ticket[0]].append(ticket[1])  # 将机票输入到字典中
        
        for key in targets:
            targets[key].sort(reverse=True)  # 对到达机场列表进行字母逆序排序
        
        result = []
        self.backtracking("JFK", targets, result)  # 调用回溯函数开始搜索路径
        return result[::-1]  # 返回逆序的行程路径
    
    def backtracking(self, airport, targets, result):
        while targets[airport]:  # 当机场还有可到达的机场时
            next_airport = targets[airport].pop()  # 弹出下一个机场
            self.backtracking(next_airport, targets, result)  # 递归调用回溯函数进行深度优先搜索
        result.append(airport)  # 将当前机场添加到行程路径中

我的代码(当天晚上理解后自己编写)

这题够难,主要是难以理解,按照used数组的解答抄一遍。

class Solution:
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:
        # 先排序,这样一旦找到一个可行路径,保证是字母排序最小的
        tickets.sort()
        used = [0]*len(tickets)
        path = ['JFK']
        cur = 'JFK'
        self.res = []
        self.backtracking(tickets,used,path,cur)

        return self.res[0]

    def backtracking(self,tickets,used,path,cur):
        if len(path)==len(tickets)+1 :
            self.res.append(path.copy())
            return True

        for i,ticket in enumerate(tickets):
            if ticket[0]==cur and used[i]==0 :
                used[i]=1
                path.append(ticket[1])
                state = self.backtracking(tickets,used,path,ticket[1])
                path.pop()
                used[i]=0
                if state:
                    return True

51 N皇后

未看解答自己编写的青春版

第一遍我竟然写出来了,虽然时间和空间,均只打败了5%

我是垃圾

import numpy as np
class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:

        if n == 1  :
            return [["Q"]]

        self.res = []
        self.temp = ['.']*n
        path = []
        idx = 0
        used = np.zeros((n,n))

        self.digui(n,idx,path,used)

        return self.res

    def digui(self,n,idx,path,used):
        if len(path)==n :
            self.res.append(path.copy())
            return
        if idx == n :
            return
        for i in range(0,n):
            if used[idx,i] > 0 :
                continue
            once = self.temp.copy()
            once[i] = 'Q'
            path.append(''.join(once))
            used = self.matrix_set(n,used,idx,i,1)
            self.digui(n,idx+1,path,used)
            path.pop()
            used = self.matrix_set(n,used,idx,i,-1)

        return 



    def matrix_set(self,n,matrix,i,j,number):

        matrix[i,:] += number
        matrix[:,j] += number
        a = i
        b = j
        while i > -1 and j > -1 :
            matrix[i,j]+=number
            i -= 1
            j -= 1
        i = a
        j = b
        while i > -1 and j < n :
            matrix[i,j]+=number
            i -= 1
            j += 1
        i = a
        j = b
        while i < n and j > -1 :
            matrix[i,j]+=number
            i += 1
            j -= 1
        i = a
        j = b

        while i < n and j < n :
            matrix[i,j]+=number
            i += 1
            j += 1

        return matrix

重点

代码随想录的解法确实比我的简单多了,既然都维护一个n*n矩阵了,为什么我要去维护哪里不能放棋子,这样一个条件呢?回溯的逻辑还不好想,要加减一,而不是简单的置01.

直接去维护皇后在哪里的棋盘不就好了吗?

太笨了!

代码随想录的代码

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        result = []  # 存储最终结果的二维字符串数组

        chessboard = ['.' * n for _ in range(n)]  # 初始化棋盘
        self.backtracking(n, 0, chessboard, result)  # 回溯求解
        return [[''.join(row) for row in solution] for solution in result]  # 返回结果集

    def backtracking(self, n: int, row: int, chessboard: List[str], result: List[List[str]]) -> None:
        if row == n:
            result.append(chessboard[:])  # 棋盘填满,将当前解加入结果集
            return

        for col in range(n):
            if self.isValid(row, col, chessboard):
                chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col+1:]  # 放置皇后
                self.backtracking(n, row + 1, chessboard, result)  # 递归到下一行
                chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col+1:]  # 回溯,撤销当前位置的皇后

    def isValid(self, row: int, col: int, chessboard: List[str]) -> bool:
        # 检查列
        for i in range(row):
            if chessboard[i][col] == 'Q':
                return False  # 当前列已经存在皇后,不合法

        # 检查 45 度角是否有皇后
        i, j = row - 1, col - 1
        while i >= 0 and j >= 0:
            if chessboard[i][j] == 'Q':
                return False  # 左上方向已经存在皇后,不合法
            i -= 1
            j -= 1

        # 检查 135 度角是否有皇后
        i, j = row - 1, col + 1
        while i >= 0 and j < len(chessboard):
            if chessboard[i][j] == 'Q':
                return False  # 右上方向已经存在皇后,不合法
            i -= 1
            j += 1

        return True  # 当前位置合法

我的代码(当天晚上理解后自己编写)

代码随想录的代码写的真不错。

我要学习的地方是 判断合理函数 的编写,因为我是从上到下填入的,所以再未填入之前,下部分的棋盘都是空的,所以判断时不必去判断整个棋盘。

仔细阅读这里的 is_valid 函数。

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        self.res = []
        chessboard = ['.' * n for _ in range(n)]
        idx = 0
        self.backtracking(n,idx,chessboard)

        return [[''.join(row) for row in solution] for solution in self.res]

    def backtracking(self,n,idx,chessboard):
        if idx == n :
            self.res.append(chessboard.copy())
            return

        for i in range(n):
            if self.is_valid(idx,i,chessboard):
            # 这里的编写原因是因为,chessboard是字符串,不是单个字符组成的列表
                chessboard[idx] = chessboard[idx][:i]+'Q'+chessboard[idx][i+1:]
                self.backtracking(n,idx+1,chessboard)
                chessboard[idx] = chessboard[idx][:i]+'.'+chessboard[idx][i+1:]
        return
    def is_valid(self,row,col,chessboard):
        for i in range(row):
            if chessboard[i][col] == 'Q':
                return False

        i,j = row-1 , col-1 
        while i>=0 and j>=0 :
            if chessboard[i][j] == 'Q':
                return False
            i-=1
            j-=1

        i,j = row-1,col+1
        while i >= 0 and j < len(chessboard):
            if chessboard[i][j] == 'Q':
                return False
            i-=1
            j+=1

        return True

37 解数独

未看解答自己编写的青春版

写的超出时间限制,不知道怎么做剪枝,剪枝在这道题目中非常重要,不剪枝简直就是循环爆炸

也不知道自己写的答案对不对,总之这里放上代码只是记录下垃圾的自己。

太恐怖了,自己本地跑了特别久都没出来,我太菜了。

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """

        idx = 0
        n = len(board)
        board = self.digui(board,idx,n)


    def digui(self,board,idx,n):
        if idx == n :
            return board
        
        for i in range(n):
            for number in range(1,10):
                if board[idx][i] != '.' :
                    continue
                number = str(number)
                if self.is_valid(board,idx,i,number,n) :
                    board[idx][i] = number
                    self.digui(board,idx+1,n)
                    board[idx][i] = '.'

        return 

    def is_valid(self,board,idx,i,number,n):
        for a in range(n):
            if board[idx][a] == number :
                return False
        for a in range(n):
           
            if board[a][i] == number :
                return False
        a = idx // 3
        b = i // 3
        for p in range(3):
            for q in range(3):
                if board[a*3+p][b*3+q] == number :
                    return False
        return True

重点

编写思想出问题了,应该是带返回值的递归。

代码随想录的代码

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        self.backtracking(board)

    def backtracking(self, board: List[List[str]]) -> bool:
        # 若有解,返回True;若无解,返回False
        for i in range(len(board)): # 遍历行
            for j in range(len(board[0])):  # 遍历列
                # 若空格内已有数字,跳过
                if board[i][j] != '.': continue
                for k in range(1, 10):
                    if self.is_valid(i, j, k, board):
                        board[i][j] = str(k)
                        if self.backtracking(board): return True
                        board[i][j] = '.'
                # 若数字1-9都不能成功填入空格,返回False无解
                return False
        return True # 有解

    def is_valid(self, row: int, col: int, val: int, board: List[List[str]]) -> bool:
        # 判断同一行是否冲突
        for i in range(9):
            if board[row][i] == str(val):
                return False
        # 判断同一列是否冲突
        for j in range(9):
            if board[j][col] == str(val):
                return False
        # 判断同一九宫格是否有冲突
        start_row = (row // 3) * 3
        start_col = (col // 3) * 3
        for i in range(start_row, start_row + 3):
            for j in range(start_col, start_col + 3):
                if board[i][j] == str(val):
                    return False
        return True

我的代码(当天晚上理解后自己编写)

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        self.backtracking(board)

    def backtracking(self,board):
        for i in range(len(board)):
            for j in range(len(board[0])):
                if board[i][j] != '.':
                    continue
                for k in range(1,10):
                    if self.is_valid(i,j,k,board):
                        board[i][j]=str(k)
                        if self.backtracking(board):
                            return True
                        board[i][j]='.'
                return False
        return True

    def is_valid(self,row,col,val,board):
        for i in range(9):
            if board[row][i] == str(val) :
                return False

        for j in range(9):
            if board[j][col] == str(val) :
                return False

        p = (row//3)*3
        q = (col//3)*3
        for i in range(p,p+3):
            for j in range(q,q+3):
                if board[i][j]==str(val):
                    return False

        return True

回溯系列大总结

这篇总结写的太好了!一定要细看!
一刷这次没仔细看。
回溯系列大总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值