获取食物的最短路径

Submission

运行时间: 80 ms

内存: 22.4 MB

from collections import deque
from typing import List

class Solution:
    def getFood(self, grid: List[List[str]]) -> int:
        m, n = len(grid), len(grid[0])
        directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]  # 右、左、下、上
        
        # 找到起始位置'*'
        start_row, start_col = -1, -1
        for i in range(m):
            for j in range(n):
                if grid[i][j] == '*':
                    start_row, start_col = i, j
                    break
            if start_row != -1:
                break
        
        # 使用队列进行BFS
        queue = deque([(start_row, start_col)])
        visited = [[False] * n for _ in range(m)]  # 记录节点是否已被访问过
        visited[start_row][start_col] = True
        steps = 0  # 记录步数
        
        while queue:
            size = len(queue)  # 当前层节点数量
            for _ in range(size):
                row, col = queue.popleft()  # 出队列
                if grid[row][col] == '#':  # 找到食物,返回步数
                    return steps
                for dx, dy in directions:  # 探索四个方向的邻近节点
                    new_row, new_col = row + dx, col + dy
                    if 0 <= new_row < m and 0 <= new_col < n and not visited[new_row][new_col] and grid[new_row][new_col] != 'X':
                        queue.append((new_row, new_col))  # 入队列
                        visited[new_row][new_col] = True  # 标记为已访问过
            steps += 1  # 完成一层搜索,步数+1
        
        # 没有找到食物路径的情况
        return -1

Explain

这个题解使用广度优先搜索(BFS)算法来解决问题。首先遍历网格,找到起始位置'*'的坐标。然后使用队列进行BFS,每次从队列中取出一个节点,探索其四个方向的相邻节点。如果找到食物'#',则返回当前的步数;如果未找到食物,则将未访问过且不是障碍物'X'的相邻节点加入队列,并标记为已访问。当队列为空时,表示无法到达食物,返回-1。

时间复杂度: O(mn)

空间复杂度: O(mn)

from collections import deque
from typing import List

class Solution:
    def getFood(self, grid: List[List[str]]) -> int:
        m, n = len(grid), len(grid[0])
        directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]  # 右、左、下、上
        
        # 找到起始位置'*'
        start_row, start_col = -1, -1
        for i in range(m):
            for j in range(n):
                if grid[i][j] == '*':
                    start_row, start_col = i, j
                    break
            if start_row != -1:
                break
        
        # 使用队列进行BFS
        queue = deque([(start_row, start_col)])  # 初始化队列,将起始位置加入队列
        visited = [[False] * n for _ in range(m)]  # 记录节点是否已被访问过
        visited[start_row][start_col] = True  # 标记起始位置为已访问
        steps = 0  # 记录步数
        
        while queue:
            size = len(queue)  # 当前层节点数量
            for _ in range(size):
                row, col = queue.popleft()  # 出队列,获取当前节点坐标
                if grid[row][col] == '#':  # 找到食物,返回步数
                    return steps
                for dx, dy in directions:  # 探索四个方向的邻近节点
                    new_row, new_col = row + dx, col + dy
                    # 检查新节点是否在网格范围内、未被访问过且不是障碍物
                    if 0 <= new_row < m and 0 <= new_col < n and not visited[new_row][new_col] and grid[new_row][new_col] != 'X':
                        queue.append((new_row, new_col))  # 将新节点加入队列
                        visited[new_row][new_col] = True  # 标记新节点为已访问
            steps += 1  # 完成一层搜索,步数+1
        
        # 没有找到食物路径的情况
        return -1

Explore

在广度优先搜索(BFS)中,队列用来确保首先探索所有距离起始点等距离的节点,然后再移向更远的节点。这保证了搜索的层级结构,即逐层向外扩展,这对于寻找最短路径是非常重要的。如果使用栈,则会变成深度优先搜索(DFS),这可能会导致首先探索远离起始点的路径,不适于最短路径的发现。而优先队列(用于实现最小优先队列的结构)通常用于最佳优先搜索(如A*搜索),这需要额外的逻辑来处理优先级(如距离评估),对于简单的最短路径问题,使用优先队列会增加不必要的复杂性和计算开销。

如果起始位置 '*' 不存在于网格中,代码在初始化队列时因找不到起始位置将导致 start_row 和 start_col 保持为 -1,这将在后续的队列操作中产生错误。为了处理这种情况,可以在完成起始位置的搜索后检查 start_row 和 start_col 是否被正确设置。如果它们仍为初始值 -1,函数应该返回 -1 或抛出一个异常,表明没有有效的起始点。

在BFS实现中,为了防止重复访问节点,使用了一个二维数组 `visited` 来跟踪每个节点的访问状态。每当一个节点被取出队列进行处理时,都会检查 `visited` 数组;如果该节点已被标记为访问过,就不会再次将其加入队列。当节点首次被发现时,会立即在 `visited` 数组中将其标记为已访问,这确保了每个节点只被处理一次。

虽然可以通过优先探索估计更接近目标的方向来优化搜索路径,但这样做实质上是在使用一种启发式搜索(如A*算法),而不是纯粹的BFS。这种优化通常涉及到计算启发式函数(如欧几里得距离或曼哈顿距离),以决定探索的优先级。这样做可以减少探索的节点总数,从而提高效率,但同时也增加了每次节点扩展的计算复杂性。对于特定类型的问题,这种方法可能更有效,但对于简单的最短路径搜索,纯粹的BFS由于其实现的简单性和可预测的性能通常更受欢迎。