332.重新安排行程
思路:
1.确定回溯函数参数:定义全局遍历存放path,
2.终止条件:遍历完所有路径,机场个数,如果达到了(航班数量+1),即path的长度应当为字符串二维数组长度 + 1
3.遍历过程:每次取出某节点后,将这个节点从备用选择中删除,防止出现循环路径
class Solution:
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
path = ["JFK"]
# defaultdic(list) 是为了方便直接append
tickets_dict = defaultdict(list)
for item in tickets:
tickets_dict[item[0]].append(item[1])
# 给每一个机场的到达机场排序,小的在前面,在回溯里首先被pop(0)出去
# 这样最先找的的path就是排序最小的答案,直接返回
for airport in tickets_dict: tickets_dict[airport].sort()
# tickets_dict里面的内容是这样的
# {'JFK': ['ATL', 'SFO'], 'SFO': ['ATL'], 'ATL': ['JFK', 'SFO']})
def backtracking(start_point):
# 终止条件
if len(path) == len(tickets) + 1:
return True
for _ in tickets_dict[start_point]:
#必须及时删除,避免出现死循环
end_point = tickets_dict[start_point].pop(0)
path.append(end_point)
# 只要找到一个就可以返回了
if backtracking(end_point):
return True
path.pop()
tickets_dict[start_point].append(end_point)
backtracking("JFK")
return path
51.N皇后
思路:
1.确定回溯函数参数:定义全局遍历存放res,参数n是棋盘的大小,然后用row来记录当前遍历到棋盘的第几层了
2.终止条件:当递归到棋盘最底层(也就是叶子节点)的时候,就可以收集结果并返回了。
3.遍历过程:
递归深度就是row控制棋盘的行,每一层里for循环的col控制棋盘的列,一行一列,确定了放置皇后的位置。
每次都是要从新的一行的起始位置开始搜,所以都是从0开始。
-
枚举出当前行所有的列。对于每一列位置:
-
约束条件:定义一个判断方法,先判断一下当前位置是否与之前棋盘上放置的皇后发生冲突,如果不发生冲突则继续放置,否则则继续向后遍历判断。
-
选择元素:选择 row, col 位置放置皇后,将其棋盘对应位置设置为 Q。
-
递归搜索:在该位置放置皇后的情况下,继续递归考虑下一行。
-
撤销选择:将棋盘上 row, col 位置设置为 . 。
-
-
验证棋盘是否合法
按照如下标准去重:
- 不能同行
- 不能同列
- 不能同斜线 (45度和135度角)
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
if not n: return []
res = []
chessboard = [['.'] * n for _ in range(n)]
def isValid(row, col, chessboard):
# 判断每列
for i in range(len(chessboard)):
if chessboard[i][col] == 'Q':
return False
# 判断左上角
i = row - 1
j = col - 1
while i>=0 and j>=0:
if chessboard[i][j] == 'Q':
return False
i -= 1
j -= 1
# 判断右上角
i = row - 1
j = col + 1
while i>=0 and j<n:
if chessboard[i][j] == 'Q':
return False
i -= 1
j += 1
return True
def backtrack(n, row, chessboard):
if row == n:
temp_res = []
for temp in chessboard:
temp_str = "".join(temp)
temp_res.append(temp_str)
res.append(temp_res)
for col in range(n):
if not isValid(row, col, chessboard):
continue
chessboard[row][col] = 'Q'
backtrack(n, row+1, chessboard)
chessboard[row][col] = '.'
backtrack(n, 0, chessboard)
return res
37.解数独
思路:
1.确定回溯函数参数:递归函数的返回值需要是bool类型,为什么呢?
因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值
2.终止条件:本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。
3.遍历过程:
枚举出当前位置填的值:
-
约束条件:判断同行,同列,9宫格是否出现该值。
-
选择元素:对board中”.“位置,赋值k(1-9)
-
递归搜索:递归判断该取值是否可以
-
撤销选择:将该位置重置为”.“
判断棋盘是否合法有如下三个维度:
- 同行是否重复
- 同列是否重复
- 9宫格里是否重复
class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
def isValid(row, col, k, board):
# 判断同行
for i in range(9):
if board[row][i] == str(k):
return False
# 判断同列
for j in range(9):
if board[j][col] == str(k):
return False
# 判断同个九宫格
startrow = (row // 3) * 3
startcol = (col // 3) * 3
for i in range(startrow, startrow + 3):
for j in range(startcol, startcol + 3):
if board[i][j] == str(k):
return False
return True
def backtrack(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 isValid(i, j, k, board):
board[i][j] = str(k)
if backtrack(board):return True
board[i][j] = "."
return False
return True
backtrack(board)