【回溯算法】应用 I
回溯算法
力扣上典型的回溯算法相关题目如下:
序号 | 题目 |
---|---|
1 | 332. 重新安排行程 |
2 | 51. N 皇后 |
3 | 37. 解数独 |
应用
应用1:Leetcode.332
题目
分析
假设有
我们定义个递归函数:
def dfs(self, ticket_num: int, trips: dict[dict[int]], path: List[str]) -> bool:
利用后序遍历的思路,去判断从当前起点开始,能否经过所有的机场。
算法步骤:
-
先对所有的机票按照字典序排序;
-
用
表 记录每一对起点到终点的数量; -
从起点开始,按照字典序回溯每一个目的机场最终能否经过所有的机场;
-
如果能到达所有的机场,则该路径满足条件;
-
如果不能到达所有的机场,则继续回溯。
-
代码实现
class Solution: def findItinerary(self, tickets: List[List[str]]) -> List[str]: trips = defaultdict(dict) # 先对tickets按照字典序排序,因为python中的dict是有序字典 tickets.sort(key = lambda x: (x[0], x[1])) for _from, _to in tickets: if _from not in trips: trips[_from] = defaultdict(int) trips[_from][_to] += 1 path = ["JFK"] self.dfs(len(tickets), trips, path) return path def dfs(self, ticket_num: int, trips: dict[dict[int]], path: List[str]) -> bool: if len(path) == ticket_num + 1: return True start = path[-1] # 优先遍历字典序靠前的目的机场,如果满足条件则加入路径中 for destination, count in trips[start].items(): if trips[start][destination] < 1: continue path.append(destination) trips[start][destination] -= 1 if self.dfs(ticket_num, trips, path): return True path.pop() trips[start][destination] += 1 return False
应用2:Leetcode.51
题目
分析
枚举每一个位置,回溯所有满足条件的位置,找到满足条件的路径,就记录下来。
算法步骤:
-
从起点开始,从左上向右下,遍历所有的列;
-
对于当前位置,检查是否可以放置皇后,同时满足如下规则:
-
当前位置的正上方的列没有放置皇后;
-
当前位置的左上方向没有放置皇后;
-
当前位置的右上方向没有放置皇后。
-
-
对于任意起点,递归的结束条件:从当前起点位置开始,所有的位置都已经处理完成,即遍历完棋盘上所有的行(
), 。 -
回溯完所有的位置后,记录满足条件的路径即可。
注:因为我们是从左上往右下的方向遍历的,所以,只需要判断已经经过的位置即可,不需要判断下方的位置。
代码实现
from typing import List class Solution: def solveNQueens(self, n: int) -> List[List[str]]: results = list() path = [["."] * n for _ in range(n)] visited = [["."] * n for _ in range(n)] self.dfs(results=results, path=path, row=0, n=n, visited=visited) return results def dfs(self, results: List[List[str]], path: List[List[str]], row: int, n: int, visited: List[List[str]]): if row == n: results.append(list(["".join(candidate) for candidate in path])) return # 遍历所有的列,colum = 0 ... n-1 for column in range(n): # 判断当前位置是否可以合法 if not self.is_valid(path, row, column): continue # 如果当前位置满足条件,继续回溯下一列 path[row][column] = "Q" self.dfs(results, path, row + 1, n, visited) path[row][column] = "." return def turn_right_top(self, start, end): """ 朝右上方检查 """ i, j = start[0], start[1] while i >= end[0] and j < end[1]: yield i, j i -= 1 j += 1 def turn_left_top(self, start, end): """ 朝左上方检查 """ i, j = start[0], start[1] while i >= end[0] and j >= end[1]: yield i, j i -= 1 j -= 1 def is_valid(self, path, row, column): """ 是否可以在位置(row, column)放置皇后 """ # 检查正上方是否有皇后冲突 for x in range(len(path)): if path[x][column] == "Q": return False # 检查右上方是否有皇后冲突 for x, y in self.turn_right_top((row - 1, column + 1), (0, len(path))): if path[x][y] == "Q": return False # 检查左上方是否有皇后冲突 for x, y in self.turn_left_top((row - 1, column - 1), (0, 0)): if path[x][y] == "Q": return False return True
应用3:Leetcode.37
题目
分析
代码实现
class Solution: def solveSudoku(self, board: List[List[str]]) -> None: """ Do not return anything, modify board in-place instead. """ self.dfs(board) return board def dfs(self, board: List[List[str]]) -> bool: # 遍历行 for i in range(len(board)): # 遍历列 for j in range(len(board[0])): # 只处理空格 if board[i][j] != ".": continue # 遍历1-9所有的数字 for num in range(1, 10): # 如果当前数字不能填在当前位置,则跳过 if not self.is_valid(board, i, j, str(num)): continue board[i][j] = str(num) if self.dfs(board): return True board[i][j] = "." # 9个数都不满足,直接返回false return False return True def is_valid(self, board, row, column, num) -> bool: # 判断行 for i in range(9): if board[i][column] == num: return False # 判断列 for j in range(9): if board[row][j] == num: return False # 九宫格的起点 start_row = (row // 3) * 3 start_column = (column // 3) * 3 # 判断当前九宫格 for i in range(start_row, start_row + 3): for j in range(start_column, start_column + 3): if board[i][j] == num: return False return True
参考:
本文作者:LARRY1024
本文链接:https://www.cnblogs.com/larry1024/p/17385980.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步