332.重新安排行程
题目链接
from collections import defaultdictclass 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) # 将当前机场添加到行程路径中
51.N皇后
题目链接
讲解链接
n皇后问题研究的是如何将n个皇后放置在n×n的棋盘上,并且使皇后彼此之间不能相互攻击。意思就是每个皇后不能处于同行、同列或同斜线上。本题的树形结构如下:
树的每一层递归对应棋盘的一行,深度对应n,宽度为n*n。
回溯三部曲:
1.递归函数参数及返回值:参数为表示棋盘的字符串列表chessboard和遍历到的行数row以及n。
def backtracking(self, chessboard, row, n):
2.递归终止条件:当row == n时,说明已经递归到了棋盘的最后一行,此时应该收集结果并返回。
if row == n:self.result.append(chessboard[:])return
3.单层递归逻辑:通过row来遍历棋盘的行,col遍历棋盘的列。每次都是从当前行的第1列开始遍历,所以col在每一轮循环中都是从0开始的。再通过isvalid函数判断当前位置放置皇后是否合法。
for col in range(0, n):if self.isvalid(chessboard, row, col):chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col + 1:]self.backtracking(chessboard, row + 1, n)chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col + 1:]
整体代码如下:
class Solution:def __init__(self):self.result = [] # 储存结果def isvalid(self, chessboard, row, col):for i in range(row): # 判断同列是否存在皇后if chessboard[i][col] == 'Q':return Falsei, j = row - 1, col - 1 # 判断左上角方向是否存在皇后while i >= 0 and j >= 0:if chessboard[i][j] == 'Q':return Falsei -= 1j -= 1i, j = row - 1, col + 1while i >= 0 and j < len(chessboard): # 判断右上角方向是否存在皇后if chessboard[i][j] == 'Q':return Falsei -= 1j += 1return Truedef backtracking(self, chessboard, row, n):if row == n: # 终止条件self.result.append(chessboard[:]) # 收集结果returnfor col in range(0, n): # 每一行都从第0列开始遍历if self.isvalid(chessboard, row, col): # 判断是否合法chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col + 1:] # 由于不能直接进行赋值,需要通过切片的方式来将row行col列位置的元素赋值为‘Q’self.backtracking(chessboard, row + 1, n)chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col + 1:] # 回溯过程,将该位置变回‘.’def solveNQueens(self, n: int) -> List[List[str]]:chessboard = ['.' * n for _ in range(n)] # 初始化棋盘self.backtracking(chessboard, 0, n)return self.result
37.解数独
题目链接
讲解链接
N皇后是因为每一行每一列只放一个皇后,只需要一层for循环遍历一行,递归来遍历列,然后一行一列确定皇后的唯一位置。本题就不一样了,本题中棋盘的每一个位置都要放一个数字(而N皇后是一行只放一个皇后),并检查数字是否合法,解数独的树形结构要比N皇后更宽更深。
回溯三部曲:
1.递归函数参数及返回值:递归函数的返回值需要是bool类型,因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值。
def backtracking(self, board):
2.递归终止条件:解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。递归的下一层的棋盘一定比上一层的棋盘多一个数,等数填满了棋盘自然就终止(填满说明找到了结果),所以不需要终止条件。
3.单层搜索逻辑:在树形图中可以看出我们需要的是一个二维的递归 (一行一列)一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!
for row in range(len(board)):for col in range(len(board[0])):if board[row][col] != '.': continuefor k in range(1, 10):if self.isvalid(row, col, k, board):board[row][col] = str(k)if self.backtracking(board):return Trueboard[row][col] = '.'return False
return True
整体代码如下:
class Solution:def isvalid(self, row, col, k, board): # 判断当前格内数字是否合法for i in range(9): # 查找同行中是否存在相同数字if board[row][i] == str(k):return Falsefor i in range(9): # 查找同列中是否存在相同数字if board[i][col] == str(k):return Falserow_k = (row // 3) * 3 # 当前位置所处九宫格的起始行col_k = (col // 3) * 3 # 当前位置所处九宫格的起始列for i in range(row_k, row_k + 3): # 判断九宫格中是否存在相同数字for j in range(col_k, col_k + 3):if board[i][j] == str(k):return Falsereturn Truedef backtracking(self, board): # 返回值为bool类型,若找到合法结果就直接返回Truefor row in range(len(board)): # 第一个for循环遍历行for col in range(len(board[0])): # 第二个for循环遍历列if board[row][col] != '.': # 如果当前位置不为空则直接跳过continuefor k in range(1, 10): # 从1到10依次填入数字if self.isvalid(row, col, k, board): # 判断当前填入的k值是否合法board[row][col] = str(k) # 将k转为字符形式填入if self.backtracking(board): # 递归向下一个空格内填入数字return True # 若返回均为True则说明找到了一组结果board[row][col] = '.' # 回溯,将之前填上的不符合条件的数字恢复为‘.’return False # 若填入1到10都不符合则说明无解return True # 遍历结束则说明填满了棋盘,找到了一组解def solveSudoku(self, board: List[List[str]]) -> None:self.backtracking(board)