首先跟随代码随想录进行了最近回溯刷题的总结,加深理解和记忆(我真的好像记性不是很好...)。回溯和递归相伴相生,在树的遍历中其实也经常涉及回溯,一开始我也很怵递归和回溯,不太习惯递归的思路,刷题下来已经形成了一些肌肉记忆,理解回溯的树形结构也很有帮助。记住,只能用回溯的题目,本质上也是在暴力枚举,只是一般的for循环没法实现,时间复杂度没有优化,最多能针对特定问题可以做一些剪枝。回溯解决的问题类型包括组合、排列、切割、子集,以及今天才刷的棋盘问题(是第一次刷题时还不敢碰的题),做题其实最需要的是透过现象看本质,不然即便知道有什么解题方法,不会用也是白费!好了,还是多刷题吧。
题目链接:332. 重新安排行程 - 力扣(Leetcode)
“本题其实是一道深度优先搜索的题目,但是我完全使用回溯法的思路来讲解这道题题目,算是给大家拓展一下思维方式,其实深搜和回溯也是分不开的,毕竟最终都是用递归。”看完讲解,第一次刷这道题真的一脸懵:还能这样?所以当下也就是照葫芦画瓢:
class Solution:
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
tickets_dict = collections.defaultdict(list)
for fr, to in tickets:
tickets_dict[fr].append(to)
# 对每个机场的到达机场列表排序,以保证最终输出是字典序最小的结果
for air in tickets_dict:
tickets_dict[air].sort()
path = ['JFK']
def backtracking(start):
if len(path) == len(tickets) + 1:
return True
for _ in tickets_dict[start]:
end = tickets_dict[start].pop(0)
path.append(end)
if backtracking(end):
return True
path.pop()
tickets_dict[start].append(end)
backtracking('JFK')
return path
题目链接:51. N 皇后 - 力扣(Leetcode)
N皇后的题目倒是容易理解,尝试不看讲解自己做题,卡在去重上了,result数组陷入了奇怪的循环(也不算循环,就是没有及时跳出怪圈),而且发现返回结果还有点问题,忘记用字符串数组的join:
class Solution:
def markAttack(self, n, grid, x, y):
# 将当前x,y上的皇后的可攻击位置标记为False
changed = [[False] * n for _ in range(n)]
def changAndMark(j, k):
if grid[j][k] == True:
grid[j][k] = False
changed[j][k] = True
# 行 列
for i in range(n):
changAndMark(x, i)
changAndMark(i, y)
# 对角线
step = 1
for i in range(y-1, -1, -1):
if x + step < n:
changAndMark(x + step, i)
if x - step >= 0:
changAndMark(x - step, i)
step += 1
step = 1
for i in range(y+1, n):
if x + step < n:
changAndMark(x + step, i)
if x - step >= 0:
changAndMark(x - step, i)
step += 1
return changed
def reverseMark(self, idx, grid, n):
for i in range(n):
for j in range(n):
if idx[i][j]:
grid[i][j] = True
def solveNQueens(self, n: int) -> List[List[str]]:
grid, path = [[True] * n for _ in range(n)], [['.'] * n for _ in range(n)]
result = []
def backtracking(num_queen):
# if sum(grid) == 0 and num_queen < n:
# return
if num_queen == n:
res = []
for p in path:
res.append(p[:])
result.append(res)
return
for i in range(n):
for j in range(n):
if not grid[i][j]:
continue # 该位置已经不能放皇后
path[i][j] = 'Q'
idx = self.markAttack(n, grid, i, j)
backtracking(num_queen + 1)
path[i][j] = '.'
self.reverseMark(idx, grid, n) # 回溯把grid中修改了的位置还原
backtracking(0)
return result
讲解的逻辑清晰多了,也不用这么多判断条件诶:
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
if not n: return []
board = [['.'] * n for _ in range(n)]
res = []
def isVaild(board,row, col):
#判断同一列是否冲突
for i in range(len(board)):
if board[i][col] == 'Q':
return False
# 判断左上角是否冲突
i = row -1
j = col -1
while i>=0 and j>=0:
if board[i][j] == 'Q':
return False
i -= 1
j -= 1
# 判断右上角是否冲突
i = row - 1
j = col + 1
while i>=0 and j < len(board):
if board[i][j] == 'Q':
return False
i -= 1
j += 1
return True
def backtracking(board, row, n):
# 如果走到最后一行,说明已经找到一个解
if row == n:
temp_res = []
for temp in board:
temp_str = "".join(temp)
temp_res.append(temp_str)
res.append(temp_res)
for col in range(n):
if not isVaild(board, row, col):
continue
board[row][col] = 'Q'
backtracking(board, row+1, n)
board[row][col] = '.'
backtracking(board, 0, n)
return res
今天在N皇后上花了不少时间,LeetCode 37.解数独这道题之后有时间再补上!