在网格中查找最短路径的算法

Algorithm to find the shortest path in a grid

背景:

问题来自leetcode:

In an N by N square grid, each cell is either empty (0) or blocked (1).

A clear path from top-left to bottom-right has length k if and only if it is composed of cells C_1, C_2, ..., C_k such that:

  • Adjacent cells C_i and C_{i+1} are connected 8-directionally (ie., they are different and share an edge or corner)
  • C_1 is at location (0, 0) (ie. has value grid[0][0])
  • C_k is at location (N-1, N-1) (ie. has value grid[N-1][N-1])
  • If C_i is located at (r, c), then grid[r][c] is empty (ie. grid[r][c] == 0).

Return the length of the shortest such clear path from top-left to bottom-right. If such a path does not exist, return -1.

问题:

我很确定我的算法是正确的,但对于这个测试用例:

[[0,1,0,0,0],[0,1,0,0,0],[0,0,0,0,1],[0,1,1,1,0],[0,1,0,0,0]]

我得到 9,正确答案是 7。我在下面的代码中做错了什么吗?

代码:

class Solution {
public:
    std::vector<std::vector<int>> dirs = {{0,1},{1,0},{-1,0},{0,-1},{1,1},{-1,-1},{1,-1},{-1,1}};
    int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
        if(grid.empty())
            return 0;

        if(grid[0][0] == 1 || grid[grid.size()-1][grid.size()-1] == 1) 
            return -1;


        int m = grid.size(), n = grid[0].size();
        std::pair<int, int> start = {0,0};
        std::pair<int, int> end = {m-1, n-1};
        std::vector<std::vector<bool>> visited(m, std::vector<bool>(n, false));
        std::priority_queue<std::pair<int,int>> q;
        q.push(start);
        visited[start.first][start.second] = true;
        int count = 1;

        while(!q.empty())
        {
            auto cur = q.top();
            q.pop();

            if(cur.first == end.first && cur.second == end.second)
                return count;

            for(auto dir : dirs)
            {
                int x = cur.first, y = cur.second;

                if(isValid(grid, x + dir[0], y + dir[1]))
                    x += dir[0], y += dir[1];

                if(!visited[x][y])
                {
                    visited[x][y] = true;
                    q.push({x,y});
                }
            }
            count++;
        }
        return -1;
    }

    bool isValid(std::vector<std::vector<int>>& grid, int i, int j)
    {
        if(i < 0 || i >= grid.size() || j < 0 || j >= grid[i].size() || grid[i][j] != 0)
            return false;
        return true;
    }
};

这不是您要使用 Dijkstra 算法的问题。该算法针对 weighted 图,而您正在处理的问题是 unweighted。此外,您使用优先级队列的方式是错误的。默认情况下,C++ 优先级队列将弹出 最大 的元素,但由于您为其提供坐标,这意味着它将弹出具有最大坐标的元素。这显然不是你需要的。事实上,您没有任何东西可以用来对节点进行排序,因为这个问题是关于 未加权 图的。

其次,count正在统计您访问的节点总数。这不可能是对的,因为您肯定还会访问不在您最终找到的最短路径上的节点。

这类问题用标准的深度优先搜索就解决了。您可以使用两个向量(不需要堆栈、队列或双端队列,...):第二个向量填充了第一个向量中所有节点的未访问邻居。该循环完成后,将第一个向量替换为第二个向量,创建新的第二个向量,然后重复……直到找到目标节点。您执行此(外部)重复的次数对应于路径的长度。

这是您的 shortestPathBinaryMatrix 函数,需要进行必要的调整才能使其正常工作:

int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
    if(grid.empty())
        return 0;

    if(grid[0][0] == 1 || grid[grid.size()-1][grid.size()-1] == 1) 
        return -1;

    int m = grid.size(), n = grid[0].size();
    pair<int, int> start = {0,0};
    pair<int, int> end = {m-1, n-1};
    vector<vector<bool>> visited(m, vector<bool>(n, false));
    // no priority queue needed: the graph is not weighted
    vector<std::pair<int,int>> q;
    q.push_back(start);
    visited[start.first][start.second] = true;
    int count = 1;

    while(!q.empty())
    {
        // just iterate the vector and populate a new one
        vector<std::pair<int,int>> q2;
        for(auto const& cur: q) {
            if(cur.first == end.first && cur.second == end.second)
                return count;
            for(auto dir : dirs)
            {
                int x = cur.first, y = cur.second;

                if(isValid(grid, x + dir[0], y + dir[1]))
                    x += dir[0], y += dir[1];

                if(!visited[x][y])
                {
                    visited[x][y] = true;
                    q2.push_back({x,y});
                }
            }
        }
        count++;
        q = q2; // prepare for next iteration
    }
    return -1;
}