LeetCode: 1091. 二进制矩阵中的最短路径

735 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第33天,点击查看活动详情

1091. 二进制矩阵中的最短路径

来源:力扣(LeetCode)

链接: leetcode.cn/problems/sh…

给你一个 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[ni][nj]=min(dp[ni][nj],dp[i][j]+1)dp[ni][nj] = min(dp[ni][nj], dp[i][j]+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:
    • 时间复杂度: O(n2)O(n^2)
    • 空间复杂度: O(n2)O(n^2)
  • 动态规划
    • 时间复杂度: O(n2)O(n^2)
    • 空间复杂度: O(n2)O(n^2)

参考