文章目录
题目
标题和出处
标题:二进制矩阵中的最短路径
难度
5 级
题目描述
要求
给定一个 n × n \texttt{n} \times \texttt{n} n×n 的二进制矩阵 grid \texttt{grid} grid,返回矩阵中最短畅通路径 的长度。如果不存在畅通路径,返回 -1 \texttt{-1} -1。
二进制矩阵中的畅通路径 是一条从左上角 单元格(即 (0, 0) \texttt{(0, 0)} (0, 0))到右下角 单元格(即 (n − 1, n − 1) \texttt{(n} - \texttt{1, n} - \texttt{1)} (n−1, n−1))的路径,该路径同时满足下述要求:
- 路径途经的所有单元格的值都是 0 \texttt{0} 0。
- 路径中所有相邻的单元格应当在 8 \texttt{8} 8 个方向之一上连通(即相邻两个单元格彼此不同且共享一条边或者一个角)。
畅通路径的长度是该路径途经的单元格总数。
示例
示例 1:

输入: grid = [[0,1],[1,0]] \texttt{grid = [[0,1],[1,0]]} grid = [[0,1],[1,0]]
输出: 2 \texttt{2} 2
示例 2:

输入: grid = [[0,0,0],[1,1,0],[1,1,0]] \texttt{grid = [[0,0,0],[1,1,0],[1,1,0]]} grid = [[0,0,0],[1,1,0],[1,1,0]]
输出: 4 \texttt{4} 4
示例 3:
输入: grid = [[1,0,0],[1,1,0],[1,1,0]] \texttt{grid = [[1,0,0],[1,1,0],[1,1,0]]} grid = [[1,0,0],[1,1,0],[1,1,0]]
输出: -1 \texttt{-1} -1
数据范围
- n = grid.length \texttt{n} = \texttt{grid.length} n=grid.length
- n = grid[i].length \texttt{n} = \texttt{grid[i].length} n=grid[i].length
- 1 ≤ n ≤ 100 \texttt{1} \le \texttt{n} \le \texttt{100} 1≤n≤100
- grid[i][j] \texttt{grid[i][j]} grid[i][j] 为 0 \texttt{0} 0 或 1 \texttt{1} 1
解法
思路和算法
边长为 n n n 的矩阵中的任意一条畅通路径都满足起点位于矩阵的左上角 ( 0 , 0 ) (0, 0) (0,0),终点位于矩阵的右下角 ( n − 1 , n − 1 ) (n - 1, n - 1) (n−1,n−1)。如果矩阵中存在畅通路径,则畅通路径的起点 ( 0 , 0 ) (0, 0) (0,0) 和终点 ( n − 1 , n − 1 ) (n - 1, n - 1) (n−1,n−1) 都必须是 0 0 0。如果 grid [ 0 ] [ 0 ] = 1 \textit{grid}[0][0] = 1 grid[0][0]=1 或 grid [ n − 1 ] [ n − 1 ] = 1 \textit{grid}[n - 1][n - 1] = 1 grid[n−1][n−1]=1,则矩阵中一定不存在畅通路径,返回 − 1 -1 −1。以下只考虑 grid [ 0 ] [ 0 ] = grid [ n − 1 ] [ n − 1 ] = 0 \textit{grid}[0][0] = \textit{grid}[n - 1][n - 1] = 0 grid[0][0]=grid[n−1][n−1]=0 的情况。
如果一条畅通路径中存在重复访问的单元格,则可以将同一个单元格的两次不同访问之间的部分的路径去掉,得到更短的畅通路径,因此最短畅通路径中一定不存在重复访问的单元格。
由于广度优先搜索的遍历顺序是和起始点的距离递增的顺序,因此可以使用广度优先搜索计算最短畅通路径的长度,做法是从左上角单元格出发执行广度优先搜索,搜索过程中维护一个与矩阵相同大小的二维数组,用于记录矩阵中的每个单元格和起点之间的最短路径长度,该二维数组称为距离数组。根据畅通路径长度的计算规则,左上角单元格对应的最短路径长度是 1 1 1。
遍历结束之后,根据右下角单元格是否已访问,返回结果。
-
如果右下角单元格已访问,则右下角单元格对应的距离数组中的元素为最短畅通路径的长度,返回该长度。
-
如果右下角单元格未访问,则不存在从左上角单元格到右下角单元格的畅通路径,返回 − 1 -1 −1。
实现方面有以下两点说明。
-
对于每个单元格需要向八个方向遍历,可以创建方向数组实现八个方向的遍历。
-
广度优先搜索需要记录每个单元格是否被访问过,这道题由于需要计算每个单元格和起始点之间的最短路径长度,因此可以根据距离数组中的元素判断每个单元格是否被访问过。具体做法是,将矩阵中的左上角单元格对应的距离数组中的元素初始化为 1 1 1,将矩阵中的其余单元格对应的距离数组中的元素初始化为无穷大,则距离数组中的值为无穷大的元素表示该单元格未访问。
代码
java
class Solution {
static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}, {-1, -1}, {1, 1}, {-1, 1}, {1, -1}};
static final int INFINITY = Integer.MAX_VALUE / 2;
public int shortestPathBinaryMatrix(int[][] grid) {
int n = grid.length;
if (grid[0][0] == 1 || grid[n - 1][n - 1] == 1) {
return -1;
}
int[][] distances = new int[n][n];
for (int i = 0; i < n; i++) {
Arrays.fill(distances[i], INFINITY);
}
distances[0][0] = 1;
Queue<int[]> queue = new ArrayDeque<int[]>();
queue.offer(new int[]{0, 0});
while (!queue.isEmpty() && distances[n - 1][n - 1] == INFINITY) {
int[] cell = queue.poll();
int row = cell[0], col = cell[1];
for (int[] dir : dirs) {
int newRow = row + dir[0], newCol = col + dir[1];
if (newRow >= 0 && newRow < n && newCol >= 0 && newCol < n && grid[newRow][newCol] == 0 && distances[newRow][newCol] == INFINITY) {
distances[newRow][newCol] = distances[row][col] + 1;
queue.offer(new int[]{newRow, newCol});
}
}
}
return distances[n - 1][n - 1] != INFINITY ? distances[n - 1][n - 1] : -1;
}
}
复杂度分析
-
时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是矩阵 grid \textit{grid} grid 的边长。广度优先搜索最多需要访问每个单元格一次。
-
空间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是矩阵 grid \textit{grid} grid 的边长。距离数组和队列需要 O ( n 2 ) O(n^2) O(n2) 的空间。