回溯系列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
回溯系列大总结
这篇总结写的太好了!一定要细看!
一刷这次没仔细看。
回溯系列大总结