📜  算法测验|须藤放置[1.8] |问题2(1)

📅  最后修改于: 2023-12-03 15:41:10.024000             🧑  作者: Mango

算法测验|须藤放置[1.8] |问题2

通过本题,你将更加深入的了解算法题目的解题思路,加深对python中的函数和数据结构的理解。

问题描述

在一个 $n \times m$ 的网格图中,每个结点可能有障碍物,你的任务是从起点到达终点的最短路径,其中每次移动代价为1,不能穿过障碍物。

请编写一个函数来计算起点到终点的最短路径长度(即移动的最少代价)。

函数签名
def shortest_path(grid: List[List[int]], start: Tuple[int, int], end: Tuple[int, int]) -> int:
    pass
输入参数
  • grid: List[List[int]],大小为 $n \times m$ 的网格图,其中 grid[i][j] 表示结点 (i,j) 是否有障碍物,0表示没有障碍,1表示存在障碍物。
  • start: Tuple[int, int],表示起点的坐标 (x,y),其中 $0 \leqslant x < n, 0 \leqslant y < m$。
  • end: Tuple[int, int],表示终点的坐标 (x,y),其中 $0 \leqslant x < n, 0 \leqslant y < m$。
输出参数
  • int,表示起点到终点的最短路径长度(即移动的最少代价),如果无法到达终点,则返回-1。
示例
input:
grid = [
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
start = (0,0)
end = (2,2)
print(shortest_path(grid, start, end))

output:
4
解题思路

本题可以使用广度优先搜索算法求解。我们首先把起点加入队列,然后从队列中依次取出结点,将其周围的结点加入队列,然后将这个结点标记为已经访问过,如果访问过的结点是终点,则直接返回移动的代价(步数)即可。

由于每个结点只需要访问一次,因此时间复杂度为 $O(nm)$,其中 $n$、$m$ 分别为矩阵的行数和列数。

根据广度优先搜索的特性,当搜索到终点时,我们就已经得到了当前的最短路径,因此不需要再继续搜索下去,可以直接返回最短路径长度。

需要注意的是,如果起点或终点是障碍物,或起点无法到达终点,这些情况都应该返回-1。

代码实现
from typing import List, Tuple
from collections import deque

def shortest_path(grid: List[List[int]], start: Tuple[int, int], end: Tuple[int, int]) -> int:
    if start == end:
        return 0
    
    rows, cols = len(grid), len(grid[0])
    if grid[start[0]][start[1]] == 1 or grid[end[0]][end[1]] == 1:
        return -1
    
    queue = deque([(start[0], start[1], 0)])
    visited = set((start[0], start[1]))
    
    while queue:
        x, y, steps = queue.popleft()
        for dx, dy in [(1,0), (-1,0), (0,1), (0,-1)]:
            nx, ny = x + dx, y + dy
            if 0 <= nx < rows and 0 <= ny < cols and grid[nx][ny] == 0 and (nx, ny) not in visited:
                if (nx, ny) == end:
                    return steps + 1
                queue.append((nx, ny, steps + 1))
                visited.add((nx, ny))

    return -1

以上是本题的具体解法和代码实现,这里仅仅给出了一种解法,相信聪明的你一定可以想出更加高效的解法。