代码随想录学习Day 26

332.重新安排行程

题目链接

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)  # 将当前机场添加到行程路径中

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 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
    def backtracking(self, chessboard, row, n):
        if row == n:  # 终止条件
            self.result.append(chessboard[:])  # 收集结果
            return
        for 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] != '.': 
            continue
        for k in range(1, 10):
            if self.isvalid(row, col, k, board):
            board[row][col] = str(k)
            if self.backtracking(board):
                return True
            board[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 False
        for i in range(9):  # 查找同列中是否存在相同数字
            if board[i][col] == str(k):
                return False
        row_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 False
        return True
    def backtracking(self, board):  # 返回值为bool类型,若找到合法结果就直接返回True
        for row in range(len(board)):  # 第一个for循环遍历行
            for col in range(len(board[0])):  # 第二个for循环遍历列
                if board[row][col] != '.':   # 如果当前位置不为空则直接跳过
                    continue
                for 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)

  • 19
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值