开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第33天,点击查看活动详情
1091. 二进制矩阵中的最短路径
来源:力扣(LeetCode)
给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1 。
二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:
- 路径途经的所有单元格都的值都是 0 。
- 路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。
- 畅通路径的长度 是该路径途经的单元格总数。
示例 1:
输入:grid = [[0,1],[1,0]] 输出:2
示例 2:
输入:grid = [[0,0,0],[1,1,0],[1,1,0]] 输出:4
示例 3:
输入:grid = [[1,0,0],[1,1,0],[1,1,0]] 输出:-1
提示:
- n == grid.length
- n == grid[i].length
- 1 <= n <= 100
- grid[i][j] 为 0 或 1
解法
- BFS: 从起点开始启动,按照八个方向进行发散,如果能够往下走,距离加一;如果遍历到右下角,且右下角不为1,则完成;
- 动态规划:考虑到最右下角的点只能从八个方向获得,状态转换方程为:
其中
dp[i][j]表示位置i,j所需要的步长; - A* Search: 相较于BFS的队列而言,这里不再使用简单的队列,这里使用优先队列,将最有前途的点,优先弹出查找;顺着终点方向,找到它的父亲,再找到父亲的父亲...,如此依次回溯,就能找到一条最佳路径。
代码实现
BFS
python实现
class Solution:
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
n = len(grid)
queue = [(0, 0, 1)]
if grid[0][0] == 1 or grid[-1][-1] == 1:
return -1
if n <= 2:
return n
dirs = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)]
while queue:
i, j, d = queue.pop(0)
for di, dj in dirs:
ni = i + di
nj = j + dj
if 0 <= ni < n and 0 <= nj < n and not grid[ni][nj]:
if ni == n-1 and nj == n-1:
return d + 1
queue.append((ni, nj, d+1))
grid[ni][nj] = 1 # 已访问
return -1
c++实现
class Solution {
public:
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
int n = grid.size();
queue<vector<int>> q;
q.push({0, 0, 1});
if (grid[0][0] == 1 || grid[n-1][n-1] == 1) return -1;
else if (n <= 2) return n;
int dirs[8][2] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
while(!q.empty()) {
vector<int> start = q.front();
int i = start[0];
int j = start[1];
int d = start[2];
q.pop();
for (auto dir: dirs) {
int ni = i + dir[0];
int nj = j + dir[1];
if (ni >= 0 && ni < n && nj >= 0 && nj < n && grid[ni][nj]==0){
if (ni == n-1 && nj == n-1) return d+1;
q.push({ni, nj, d+1});
grid[ni][nj] = 1; // 已访问
}
}
}
return -1;
}
};
动态规划
python实现
class Solution:
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
n = len(grid)
if grid[0][0] or grid[-1][-1]:
return -1
elif n <= 2:
return n
dp = [[float('INF')] * n for _ in range(n)] # 初始化时候每个位置都是最大化
dp[0][0] = 1
dirs = [(0, -1), (0, 1), (1, 0), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)]
queue = [(0, 0)]
while queue:
i, j = queue.pop(0)
for di, dj in dirs:
ni = i + di
nj = j + dj
if 0 <= ni < n and 0 <= nj < n and grid[ni][nj] == 0:
dp[ni][nj] = min(dp[ni][nj], dp[i][j]+1)
grid[ni][nj] = 1
queue.append((ni, nj))
return dp[-1][-1] if dp[-1][-1] < float('INF') else -1 # 如果遍历完值还是最大值,说明没能成功
c++实现
class Solution {
public:
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
int n = grid.size();
queue<vector<int>> q;
q.push({0, 0});
if (grid[0][0] == 1 || grid[n-1][n-1] == 1) return -1;
else if (n <= 2) return n;
int dirs[8][2] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
vector<vector<int>> dp(n, vector<int> (n, INT_MAX));
dp[0][0] = 1;
while(!q.empty()) {
vector<int> start = q.front();
int i = start[0];
int j = start[1];
q.pop();
for (auto dir: dirs) {
int ni = i + dir[0];
int nj = j + dir[1];
if (ni >= 0 && ni < n && nj >= 0 && nj < n && grid[ni][nj]==0){
dp[ni][nj] = min(dp[ni][nj], dp[i][j]+1);
q.push({ni, nj});
grid[ni][nj] = 1;
}
}
}
return dp[n-1][n-1] < INT_MAX ? dp[n-1][n-1]: -1;
}
};
A* Search
python实现
class Solution:
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
if grid[0][0] or grid[-1][-1]: return -1
n = len(grid)
target = (n - 1, n - 1)
def distance(x, y):
return max(abs(x - (n - 1)), abs(y - (n - 1))) # 优先队列,pop的时候返回最小值,说明该点离终点越近
pq = []
heapq.heappush(pq, (distance(0, 0), 0, 0, 0)) # priority,step,r,c
costs = {}
costs[(0, 0)] = 0
best = set()
while pq:
pri, step, r, c = heapq.heappop(pq)
if (r, c) == target:
return step + 1
best.add((r, c))
for nr, nc in ((r - 1, c - 1), (r - 1, c), (r - 1, c + 1), (r, c - 1), (r, c + 1), (r + 1, c - 1),
(r + 1, c), (r + 1, c + 1)):
if 0 <= nr < n and 0 <= nc < n and (nr, nc) not in best and grid[nr][nc] == 0:
if (nr, nc) not in costs or costs[(r, c)] + 1 < costs[(nr, nc)]:
costs[(nr, nc)] = costs[(r, c)] + 1
priority = costs[(nr, nc)] + distance(nc, nc)
heapq.heappush(pq, (priority, step + 1, nr, nc))
return -1
c++实现
class Solution {
public:
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
//int n=grid.size();
const int n = grid.size();
if (grid[0][0] == 0 && n == 1)
return 1;
if (grid[0][0] == 1 || grid.back().back() == 1)
return -1;
vector<vector<int>> dist(n,vector<int>(n,1e9));
queue<pair<int,pair<int,int>>> q;
q.push({1,{0,0}});
dist[0][0]=0;
int dRow[8]={-1,-1,0,1,1,1,0,-1};
int dCol[8]={0,1,1,1,0,-1,-1,-1};
while(!q.empty())
{
int dis=q.front().first;
int r=q.front().second.first;
int c=q.front().second.second;
q.pop();
for(int i=0;i<8;i++)
{
int nrow=r+dRow[i];
int ncol=c+dCol[i];
if(nrow>=0 && nrow<n && ncol>=0 && ncol<n && grid[nrow][ncol]==0
&& dis+1<dist[nrow][ncol])
{
dist[nrow][ncol]=dis+1;
if(nrow==n-1 && ncol==n-1) return dist[nrow][ncol];
q.push({1+dis,{nrow,ncol}});
}
}
}
return -1;
}
};
复杂度分析
- BFS:
- 时间复杂度:
- 空间复杂度:
- 动态规划
- 时间复杂度:
- 空间复杂度: