LeetCode 1730中等图/网格BFS
获取食物的最短路径 图解题解
这道题到底在问什么
给定字符网格 grid:* 起点、# 食物、O 空地、X 障碍。返回从起点到最近食物的最短步数;走不到返回 −1。
- 输入
- [[X,X,X],[X,*,#],[X,X,X]]
- 输出
- 1(右边就是食物)
最优解:一步一步想明白
- 3记住「队头取格 → 扩四邻 → 空地入队、食物即止」,第一次到食物就是最短。下面每帧扩一个格。
- 4起点 *(紫,正在处理)入队即标记已访问,步数 0。食物 # 在右下角,开始一圈圈往外扩。
- 5队头取出 (0,0)(紫,步数 0),看四邻:新空地邻居 (1,0)、(0,1) 入队即标蓝(已访问),步数记 1。
- 6队头取出 (1,0)(紫,步数 1),看四邻:新空地邻居 (2,0) 入队即标蓝(已访问),步数记 2。
- 7队头取出 (0,1)(紫,步数 1),看四邻:新空地邻居 (0,2) 入队即标蓝(已访问),步数记 2。
- 8队头取出 (2,0)(紫,步数 2),看四邻:新空地邻居 (3,0) 入队即标蓝(已访问),步数记 3。
- 9队头取出 (0,2)(紫,步数 2),看四邻:新空地邻居 (1,2)、(0,3) 入队即标蓝(已访问),步数记 3。
- 10队头取出 (3,0)(紫,步数 3),看四邻:新空地邻居 (4,0) 入队即标蓝(已访问),步数记 4。
- 11队头取出 (1,2)(紫,步数 3),看四邻:新空地邻居 (2,2) 入队即标蓝(已访问),步数记 4。
- 12队头取出 (0,3)(紫,步数 3),看四邻:四邻无新格,跳过。
- 13队头取出 (4,0)(紫,步数 4),看四邻:新空地邻居 (4,1) 入队即标蓝(已访问),步数记 5。
- 14队头取出 (2,2)(紫,步数 4),看四邻:新空地邻居 (2,3) 入队即标蓝(已访问),步数记 5。
- 15队头取出 (4,1)(紫,步数 5),看四邻:新空地邻居 (4,2) 入队即标蓝(已访问),步数记 6。
- 16队头取出 (2,3)(紫,步数 5),看四邻:新空地邻居 (2,4) 入队即标蓝(已访问),步数记 6。
- 17队头取出 (4,2)(紫,步数 6),看四邻:新空地邻居 (4,3) 入队即标蓝(已访问),步数记 7。
- 18队头取出 (2,4)(紫,步数 6),看四邻:新空地邻居 (3,4)、(1,4) 入队即标蓝(已访问),步数记 7。
- 19队头取出 (4,3)(紫,步数 7),看四邻:新空地邻居 (4,4) 入队即标蓝(已访问),步数记 8。
- 20队头取出 (3,4)(紫,步数 7),看四邻:四邻无新格,跳过。
- 21队头取出 (1,4)(紫,步数 7),看四邻:新空地邻居 (1,5) 入队即标蓝(已访问),步数记 8。
- 22队头取出 (4,4)(紫,步数 8),看四邻:新空地邻居 (4,5) 入队即标蓝(已访问),步数记 9。
- 23队头取出 (1,5)(紫,步数 8),看四邻:新空地邻居 (0,5)、(1,6) 入队即标蓝(已访问),步数记 9。
- 24从 (4,5) 看四邻碰到食物 #,返回 9 + 1 = 10 步。逐层扩展,首次到达即最短。
- 25顺着「从谁来的」回溯,绿色这条就是最短路,从起点到食物共 10 步。
⚠️ 容易写错的地方
✗ 错:用 DFS 找最短路
✓ 对:最短步数用 BFS(逐层)
DFS 先扎到底,第一次到达不保证最短;BFS 按距离扩展,首次到达即最短
✗ 错:入队时不立刻标记已访问
✓ 对:入队即标记
否则同一格会被多次入队,可能退化甚至重复计数
✗ 错:忘了处理「走不到食物」的情况
✓ 对:队列空了仍没碰到 #,返回 −1
起点可能被障碍围死,不可达必须返回 −1
完整代码(Python / C++ / Java)
Python
from typing import List
from collections import deque
class Solution:
def getFood(self, grid: List[List[str]]) -> int:
if not grid or not grid[0]:
return -1
m, n = len(grid), len(grid[0])
q = deque()
for i in range(m):
for j in range(n):
if grid[i][j] == '*':
q.append((i, j, 0))
grid[i][j] = 'X'
while q:
i, j, d = q.popleft()
for di, dj in ((1,0),(-1,0),(0,1),(0,-1)):
ni, nj = i + di, j + dj
if 0 <= ni < m and 0 <= nj < n and grid[ni][nj] != 'X':
if grid[ni][nj] == '#':
return d + 1
grid[ni][nj] = 'X'
q.append((ni, nj, d + 1))
return -1C++
#include <vector>
#include <queue>
#include <tuple>
using namespace std;
class Solution {
public:
int getFood(vector<vector<char>>& grid) {
if (grid.empty() || grid[0].empty()) return -1;
int m = grid.size(), n = grid[0].size();
queue<tuple<int,int,int>> q;
for (int i = 0; i < m; ++i) for (int j = 0; j < n; ++j) if (grid[i][j] == '*') { q.push({i,j,0}); grid[i][j] = 'X'; }
int dirs[5] = {1,0,-1,0,1};
while (!q.empty()) {
auto [i,j,d] = q.front(); q.pop();
for (int k = 0; k < 4; ++k) {
int ni = i + dirs[k], nj = j + dirs[k+1];
if (ni >= 0 && ni < m && nj >= 0 && nj < n && grid[ni][nj] != 'X') {
if (grid[ni][nj] == '#') return d + 1;
grid[ni][nj] = 'X';
q.push({ni,nj,d+1});
}
}
}
return -1;
}
};Java
import java.util.*;
class Solution {
public int getFood(char[][] grid) {
if (grid.length == 0 || grid[0].length == 0) return -1;
int m = grid.length, n = grid[0].length;
Queue<int[]> q = new ArrayDeque<>();
for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) if (grid[i][j] == '*') { q.offer(new int[]{i,j,0}); grid[i][j] = 'X'; }
int[] dirs = {1,0,-1,0,1};
while (!q.isEmpty()) {
int[] cur = q.poll();
for (int k = 0; k < 4; k++) {
int ni = cur[0] + dirs[k], nj = cur[1] + dirs[k+1];
if (ni >= 0 && ni < m && nj >= 0 && nj < n && grid[ni][nj] != 'X') {
if (grid[ni][nj] == '#') return cur[2] + 1;
grid[ni][nj] = 'X';
q.offer(new int[]{ni,nj,cur[2]+1});
}
}
}
return -1;
}
}复杂度
时间
O(m·n)
每个格子最多入队、出队一次
空间
O(m·n)
队列与已访问标记
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 获取食物的最短路径 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
如果有多个起点怎么办(本题只有一个 *,这是可迁移的扩展知识)?+
本题约束只有一个起点。若遇到多起点变体,可把所有起点一开始就全部入队、步数都记 0(多源 BFS),扩展时天然取到「离任意起点最近」的距离,无需对每个起点各跑一次 BFS。
BFS 和 Dijkstra 什么关系?+
本题每步代价都是 1(网格四邻),BFS 就够且最优 O(mn)。若每条边权重不同,才需要 Dijkstra(优先队列按累计代价扩展)。边权全为 1 时 Dijkstra 退化成 BFS。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 获取食物的最短路径 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。