Background:
The problem is from 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 cellsC_1, C_2, ..., C_k
such that:
- Adjacent cells
C_i
andC_{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 valuegrid[0][0]
)C_k
is at location(N-1, N-1)
(ie. has valuegrid[N-1][N-1]
)- If
C_i
is located at(r, c)
, thengrid[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.
Question:
I was quite certain that my algorithm was correct but for this test case:
[[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]]
I get 9, and the correct answer is 7. Is there something I am doing wrong in the code below?
Code:
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;
}
};
This is not a problem for which you would use Dijkstra's algorithm. That algorithm is targetting weighted graphs, while the problem you are dealing with is unweighted. Moreover, the way you use a priority queue is wrong. A C++ priority queue will by default pop the element that is largest, but since you provide it coordinates, that means it will pop the element with the largest coordinates. This is obviously not what you need. In fact, you do not have anything to order nodes by, since this problem is about an unweighted graph.
Secondly, count
is counting the total number of nodes you visit. That cannot be right, since you surely also visit nodes that are not on the shortest path that you eventually find.
This kind of problem is solved with a standard depth-first search. You can do it with two vectors (no need for stack, queue or deque, ...): the second vector gets populated with the unvisited neighbors of all the nodes in the first. Once that cycle is completed, you replace the first vector with the second, create a new second vector, and repeat... until you find the target node. The number of times you do this (outer) repetition corresponds to the length of the path.
Here is your shortestPathBinaryMatrix
function with the necessary adaptations to make it work:
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;
}