代码随想录算法训练营Day 29 || 332.重新安排行程、51. N皇后、37. 解数独

332.重新安排行程

力扣题目链接(opens new window)

给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。

提示:

  • 如果存在多种有效的行程,请你按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
  • 所有的机场都用三个大写字母表示(机场代码)。
  • 假定所有机票至少存在一种合理的行程。
  • 所有的机票必须都用一次 且 只能用一次。

示例 1:

  • 输入:[["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
  • 输出:["JFK", "MUC", "LHR", "SFO", "SJC"]

示例 2:

  • 输入:[["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
  • 输出:["JFK","ATL","JFK","SFO","ATL","SFO"]
  • 解释:另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后。

解题思路:

1. 把机票看作箭头:
首先,你可以把每张机票看作一个箭头,箭头的尾部是出发机场,箭头的头部是到达机场。

2. 构建机场的连接图:
我们可以把所有机票放在一张图上,每一个机场是一个点,每一张机票就是从一个点指向另一个点的箭头。

3. 从 JFK 开始:
题目说我们的旅行始终从 JFK 开始,所以我们从 JFK 这个点开始,尝试走向其他的点。

4. 选择下一个机场:
每次选择下一个机场时,我们都会选择字母顺序最小的那个,这样保证我们得到的行程是字典序最小的。

5. 递归地探索:
从 JFK 开始,我们会递归地探索每一个可以去的机场,直到我们用完了所有的机票。

6. 回溯并记录行程:
当我们到达某个机场后发现没有办法继续前进(即没有后续机票),或者已经用完了所有机票,我们就把当前机场记录下来,并返回到上一个机场,尝试其他可能的路径。

7. 得到的行程是反的:
由于我们是在回溯时才记录机场,所以得到的行程实际上是反的。例如,我们可能先得到 SJC,然后是 SFO,最后才是 JFK。因此,在返回答案之前,需要将行程反转。

8. 结果就是我们的行程:
最后,按照上述方法,我们可以得到一条经过所有机票的、字母顺序最小的行程。

from typing import List
import heapq

class Solution:
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:
        # 创建一个映射,来存储从一个地点到其他所有地点的票
        adj = {}
        for start, end in tickets:
            if start not in adj:
                adj[start] = []
            heapq.heappush(adj[start], end)  # 用堆,保证字典序最小的机场优先出队

        path = []

        def dfs(airport):
            # 访问机场的所有邻居机场,直到当前机场没有邻居为止
            while airport in adj and adj[airport]:
                dfs(heapq.heappop(adj[airport]))  # 从堆中取出字典序最小的机场,并从邻接表中移除
            path.append(airport)  # 添加到路径中

        dfs("JFK")
        return path[::-1]  # 返回逆序的路径

例子:tickets = [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]

  1. 构建邻接表,将每张票的起点和终点添加到字典中:

adj = { "JFK": ["ATL", "SFO"], "SFO": ["ATL"], "ATL": ["JFK", "SFO"] }

注意,由于我们使用了堆,因此每个机场的列表中,机场是按照字典序排列的。

  1. 从"JFK"开始进行深度优先搜索。

  2. 在搜索的过程中,每访问一个机场,就将该机场从邻接表中的当前机场列表中移除,并递归访问新的机场。

  3. 当一个机场没有邻接的机场时,将其添加到path中。

  4. 搜索结束后,path的逆序即为答案。

代码逻辑和Python语法:

  • 使用heapq模块创建了一个优先队列,确保始终选择字典序最小的机场。
  • dfs函数中,我们递归地访问每个机场的所有邻接机场。
  • 使用heapq.heappop函数从堆中取出字典序最小的机场,并从邻接表中移除它。
  • 当一个机场没有邻接的机场时,将其添加到path中。
  • 最后,返回path的逆序作为答案,因为在深度优先搜索中,我们实际上是在访问完所有邻接机场后才添加当前机场到路径中的。

 

51. N皇后

力扣题目链接(opens new window)

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:

  • 输入:n = 4
  • 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
  • 解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

  • 输入:n = 1
  • 输出:[["Q"]]

"N 皇后"问题是一个经典的回溯算法问题,其目标是在一个 n×n 的棋盘上放置 n 个皇后,使得它们不互相攻击。皇后可以攻击同一行、同一列和同一对角线上的其他棋子。解决这个问题的思路如下:

  1. 创建一个空的 n×n 棋盘,用一个二维数组来表示,初始时所有位置都标记为".",表示空位。

  2. 从第一行开始,逐行放置皇后。对于每一行,从左到右依次尝试将皇后放在不会受到攻击的位置。

  3. 在放置皇后的过程中,需要检查当前位置是否满足以下条件:

    • 同一列没有其他皇后。
    • 左上对角线上没有其他皇后。
    • 右上对角线上没有其他皇后。
  4. 如果找到一个位置可以放置皇后,就将该位置标记为"Q",表示皇后的位置,然后进入下一行继续尝试放置下一个皇后。

  5. 如果无法找到合适的位置放置皇后,回溯到上一行,重新选择其他位置放置上一行的皇后,并继续尝试下一列。

  6. 当成功放置了 n 个皇后时,即完成了一种解决方案,将当前棋盘的状态保存下来,然后继续尝试下一种解决方案。

  7. 重复以上步骤,直到找到所有合法的解决方案。

  8. 最后,返回所有找到的解决方案。

这个思路通过回溯算法实现,不断尝试不同的位置,当发现无法继续放置皇后时,回溯到上一步,直到找到所有可能的解决方案为止。这种方法确保了找到的解决方案满足题目的要求,即皇后不互相攻击。

from typing import List

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        # 初始化一个空棋盘,用二维数组表示
        board = [['.' for _ in range(n)] for _ in range(n)]
        # 用于存储所有解决方案
        solutions = []

        def is_valid(row, col):
            # 检查同一列是否有皇后
            for i in range(row):
                if board[i][col] == 'Q':
                    return False

            # 检查左上对角线是否有皇后
            i, j = row - 1, col - 1
            while i >= 0 and j >= 0:
                if board[i][j] == 'Q':
                    return False
                i -= 1
                j -= 1

            # 检查右上对角线是否有皇后
            i, j = row - 1, col + 1
            while i >= 0 and j < n:
                if board[i][j] == 'Q':
                    return False
                i -= 1
                j += 1

            return True

        def backtrack(row):
            if row == n:
                # 找到一个解决方案,将棋盘状态添加到结果列表中
                solutions.append([''.join(row) for row in board])
                return

            for col in range(n):
                if is_valid(row, col):
                    # 在当前位置放置皇后
                    board[row][col] = 'Q'
                    # 继续下一行的放置
                    backtrack(row + 1)
                    # 回溯,重置当前位置
                    board[row][col] = '.'

        # 从第一行开始放置皇后
        backtrack(0)
        return solutions

 

37. 解数独

力扣题目链接

编写一个程序,通过填充空格来解决数独问题。

一个数独的解法需遵循如下规则: 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 空白格用 '.' 表示。

解数独

一个数独。

解数独

答案被标成红色。

提示:

  • 给定的数独序列只包含数字 1-9 和字符 '.' 。
  • 你可以假设给定的数独只有唯一解。
  • 给定数独永远是 9x9 形式的。
from typing import List

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        :param board: List[List[str]]
        :return: None
        """
        self.solve(board)

    def solve(self, board: List[List[str]]) -> bool:
        for i in range(9):
            for j in range(9):
                if board[i][j] == ".":
                    for num in map(str, range(1, 10)):
                        if self.is_valid(board, i, j, num):
                            board[i][j] = num
                            if self.solve(board):
                                return True
                            board[i][j] = "."
                    return False
        return True

    def is_valid(self, board: List[List[str]], row: int, col: int, num: str) -> bool:
        for i in range(9):
            if board[i][col] == num or board[row][i] == num:
                return False
        row_start, col_start = 3 * (row // 3), 3 * (col // 3)
        for i in range(row_start, row_start + 3):
            for j in range(col_start, col_start + 3):
                if board[i][j] == num:
                    return False
        return True

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值