Given an n x m matrix grid where each cell contains either 0 or 1, determine the shortest distance between a source cell and a destination cell. You can move to an adjacent cell (up, down, left, or right) if that adjacent cell has a value of 1. The path can only be created out of cells containing 1. If the destination cell is not reachable from the source cell, return -1.
Input: grid = [[1, 1, 1, 1],[1, 1, 0, 1],[1, 1, 1, 1],[1, 1, 0, 0],[1, 0, 0, 1]], source = [0, 1], destination = [2, 2]
Output: 3
Explanation: The shortest path from (0, 1) to (2, 2) is:
Move down to (1, 1)
Move down to (2, 1)
Move right to (2, 2)
Thus, the shortest distance is 3
Input: grid = [[1, 1, 1, 1, 1],[1, 1, 1, 1, 1],[1, 1, 1, 1, 0],[1, 0, 1, 0, 1]], source = [0, 0], destination = [3, 4]
Output: -1
Explanation:
Since, there is no path possible between the source cell and the destination cell, hence we return -1.
Input: grid = [[1, 0, 1],[1, 1, 0],[1, 1, 1]], source = [0, 0], destination = [2, 2]
Pre Requisite: Shortest path in undirected graph with unit weights
The problem includes finding the shortest path from the source to destination which gives the idea of using a Dijsktra's Algorithm. But since, all the edges are of unit weight, instead of using Dijsktra's algorithm, a simple BFS traversal will get the job done.
This involves using the Queue data structure in place of the Min-heap data structure improving the time complexity. As BFS traversal visits all cells in a level order fashion, it will ensure that whenever the destination cell is reached, it is reached via the shortest path.
A distance array will be used to store the shortest path of the intermediate nodes from the source node which will also be used to identify if a shorter path is found leading to the destination cell.
delRow = {-1, 0, 1, 0}
delCol = {0, 1, 0, -1}
#include <bits/stdc++.h>
using namespace std;
class Solution{
private:
// Delta row and column array
vector<int> delRow = {-1, 0, 1, 0};
vector<int> delCol = {0, -1, 0, 1};
// Function to check if a cell is valid
bool isValid(int &row, int &col,
int &n, int &m) {
// Return false if the cell is invalid
if(row < 0 || row >= n) return false;
if(col < 0 || col >= m) return false;
// Return true if the cell is valid
return true;
}
public:
/* Function to determine the shortest distance
between source and destination */
int shortestPath(vector<vector<int>> &grid, pair<int, int> source,
pair<int, int> destination) {
// Edge Case
if (source.first == destination.first &&
source.second == destination.second)
return 0;
/* Queue data structure to store the pairs of the
form: {dist, {coordinates of cell}} */
queue<pair<int, pair<int, int>>> q;
// Dimensions of grid
int n = grid.size();
int m = grid[0].size();
// Distance matrix
vector<vector<int>> dist(n, vector<int>(m, 1e9));
// Distane of source from itself is zero
dist[source.first][source.second] = 0;
// Add the surce to queue
q.push({0, {source.first, source.second}});
// Until the queue is empty
while (!q.empty()) {
// Get the element
auto it = q.front();
q.pop();
int dis = it.first; // Distance
int row = it.second.first; // Row of cell
int col = it.second.second; // Column of cell
// Iterate through all the neighbors
for (int i = 0; i < 4; i++){
// Coordinates of the new cell
int newRow = row + delRow[i];
int newCol = col + delCol[i];
/* Checking the validity of the cell and
updating if a shorter distance is found */
if (isValid(newRow, newCol, n, m) &&
grid[newRow][newCol] == 1 &&
dis + 1 < dist[newRow][newCol]) {
// Update the distance
dist[newRow][newCol] = 1 + dis;
// Return the distance is the destination is reached
if (newRow == destination.first &&
newCol == destination.second)
return dis + 1;
// Add the new cell to queue
q.push({1 + dis, {newRow, newCol}});
}
}
}
// If no path is found from source to destination
return -1;
}
};
int main() {
pair<int, int> source, destination;
source.first = 0;
source.second = 1;
destination.first = 2;
destination.second = 2;
vector<vector<int>> grid = {
{1, 1, 1, 1},
{1, 1, 0, 1},
{1, 1, 1, 1},
{1, 1, 0, 0},
{1, 0, 0, 1}
};
/* Creating an instance of
Solution class */
Solution sol;
/* Function call to determine the shortest
distance between source and destination */
int ans = sol.shortestPath(grid, source, destination);
cout << "The shortest distance from the source to destination is: " << ans;
return 0;
}
import java.util.*;
class Solution {
// Delta row and column array
private int[] delRow = {-1, 0, 1, 0};
private int[] delCol = {0, -1, 0, 1};
// Function to check if a cell is valid
private boolean isValid(int row, int col,
int n, int m) {
// Return false if the cell is invalid
if (row < 0 || row >= n) return false;
if (col < 0 || col >= m) return false;
// Return true if the cell is valid
return true;
}
/* Function to determine the shortest distance
between source and destination */
public int shortestPath(int[][] grid, int[] source,
int[] destination) {
// Edge Case
if (source[0] == destination[0] &&
source[1] == destination[1])
return 0;
/* Queue data structure to store the pairs of the
form: {dist, {coordinates of cell}} */
Queue<int[]> q = new LinkedList<>();
// Dimensions of grid
int n = grid.length;
int m = grid[0].length;
// Distance matrix
int[][] dist = new int[n][m];
for (int[] row : dist) {
Arrays.fill(row, Integer.MAX_VALUE);
}
// Distance of source from itself is zero
dist[source[0]][source[1]] = 0;
// Add the source to queue
q.add(new int[]{0, source[0], source[1]});
// Until the queue is empty
while (!q.isEmpty()) {
// Get the element
int[] it = q.poll();
int dis = it[0]; // Distance
int row = it[1]; // Row of cell
int col = it[2]; // Column of cell
// Iterate through all the neighbors
for (int i = 0; i < 4; i++) {
// Coordinates of the new cell
int newRow = row + delRow[i];
int newCol = col + delCol[i];
/* Checking the validity of the cell and
updating if a shorter distance is found */
if (isValid(newRow, newCol, n, m) &&
grid[newRow][newCol] == 1 &&
dis + 1 < dist[newRow][newCol]) {
// Update the distance
dist[newRow][newCol] = 1 + dis;
/* Return the distance if the
destination is reached */
if (newRow == destination[0] &&
newCol == destination[1])
return dis + 1;
// Add the new cell to queue
q.add(new int[]{1 + dis, newRow, newCol});
}
}
}
// If no path is found from source to destination
return -1;
}
public static void main(String[] args) {
int[] source = {0, 1};
int[] destination = {2, 2};
int[][] grid = {
{1, 1, 1, 1},
{1, 1, 0, 1},
{1, 1, 1, 1},
{1, 1, 0, 0},
{1, 0, 0, 1}
};
/* Creating an instance of
Solution class */
Solution sol = new Solution();
/* Function call to determine the shortest
distance between source and destination */
int ans = sol.shortestPath(grid, source, destination);
System.out.println("The shortest distance from the source to destination is: " + ans);
}
}
from collections import deque
class Solution:
# Delta row and column array
delRow = [-1, 0, 1, 0]
delCol = [0, -1, 0, 1]
# Function to check if a cell is valid
def isValid(self, row, col, n, m):
# Return false if the cell is invalid
if row < 0 or row >= n: return False
if col < 0 or col >= m: return False
# Return true if the cell is valid
return True
# Function to determine the shortest distance
# between source and destination
def shortestPath(self, grid, source, destination):
# Edge Case
if source == destination:
return 0
# Queue data structure to store the pairs of the
# form: {dist, {coordinates of cell}}
q = deque()
# Dimensions of grid
n = len(grid)
m = len(grid[0])
# Distance matrix
dist = [[float('inf')] * m for _ in range(n)]
# Distance of source from itself is zero
dist[source[0]][source[1]] = 0
# Add the source to queue
q.append((0, source[0], source[1]))
# Until the queue is empty
while q:
# Get the element
dis, row, col = q.popleft()
# Iterate through all the neighbors
for i in range(4):
# Coordinates of the new cell
newRow = row + self.delRow[i]
newCol = col + self.delCol[i]
# Checking the validity of the cell and
# updating if a shorter distance is found
if (self.isValid(newRow, newCol, n, m) and
grid[newRow][newCol] == 1 and
dis + 1 < dist[newRow][newCol]):
# Update the distance
dist[newRow][newCol] = 1 + dis
# Return the distance if the destination is reached
if (newRow, newCol) == destination:
return dis + 1
# Add the new cell to queue
q.append((1 + dis, newRow, newCol))
# If no path is found from source to destination
return -1
if __name__ == "__main__":
source = (0, 1)
destination = (2, 2)
grid = [
[1, 1, 1, 1],
[1, 1, 0, 1],
[1, 1, 1, 1],
[1, 1, 0, 0],
[1, 0, 0, 1]
]
# Creating an instance of
# Solution class
sol = Solution()
# Function call to determine the shortest
# distance between source and destination
ans = sol.shortestPath(grid, source, destination)
print(f"The shortest distance from the source to destination is: {ans}")
class Solution {
// Delta row and column array
delRow = [-1, 0, 1, 0];
delCol = [0, -1, 0, 1];
// Function to check if a cell is valid
isValid(row, col, n, m) {
// Return false if the cell is invalid
if (row < 0 || row >= n) return false;
if (col < 0 || col >= m) return false;
// Return true if the cell is valid
return true;
}
// Function to determine the shortest distance
// between source and destination
shortestPath(grid, source, destination) {
// Edge Case
if (source[0] === destination[0] &&
source[1] === destination[1])
return 0;
// Queue data structure to store the pairs of the
// form: {dist, {coordinates of cell}}
const q = [];
// Dimensions of grid
const n = grid.length;
const m = grid[0].length;
// Distance matrix
const dist = Array.from(
{ length: n },
() => Array(m).fill(Infinity)
);
// Distance of source from itself is zero
dist[source[0]][source[1]] = 0;
// Add the source to queue
q.push([0, source[0], source[1]]);
// Until the queue is empty
while (q.length > 0) {
// Get the element
const [dis, row, col] = q.shift();
// Iterate through all the neighbors
for (let i = 0; i < 4; i++) {
// Coordinates of the new cell
const newRow = row + this.delRow[i];
const newCol = col + this.delCol[i];
// Checking the validity of the cell and
// updating if a shorter distance is found
if (this.isValid(newRow, newCol, n, m) &&
grid[newRow][newCol] === 1 &&
dis + 1 < dist[newRow][newCol]) {
// Update the distance
dist[newRow][newCol] = 1 + dis;
// Return the distance if the destination is reached
if (newRow === destination[0] &&
newCol === destination[1])
return dis + 1;
// Add the new cell to queue
q.push([1 + dis, newRow, newCol]);
}
}
}
// If no path is found from source to destination
return -1;
}
}
const main = () => {
const source = [0, 1];
const destination = [2, 2];
const grid = [
[1, 1, 1, 1],
[1, 1, 0, 1],
[1, 1, 1, 1],
[1, 1, 0, 0],
[1, 0, 0, 1]
];
// Creating an instance of
// Solution class
const sol = new Solution();
// Function call to determine the shortest
// distance between source and destination
const ans = sol.shortestPath(grid, source, destination);
console.log(`The shortest distance from the source to destination is: ${ans}`);
};
main();
Time Complexity: O(N*M) (where N and M are the dimensions of the grid)
A simple BFS traversal takes O(V+E) time where V and E represent the number of cells and number of edges.
Because, V = N*M and E = 4*N*M, the overall time complexity is O(N*M).
Space Complexity: O(N*M) The distance array takes O(N*M) space and the queue space can go upto O(N*M) in the worst case.
Q: Can we use Dijkstra’s Algorithm instead of BFS?
A: Since all moves have the same weight (1), BFS is optimal. Dijkstra’s is required only when edge weights vary.
Q: What if multiple shortest paths exist?
A: BFS naturally finds one of the shortest paths, but if all paths are required, additional backtracking from the destination would be needed.
Q: What if obstacles could be removed at a cost?
A: This would require a modified Dijkstra’s Algorithm, where removing obstacles adds weight to certain paths.
Q: What if multiple sources and multiple destinations existed?
A: Use multi-source BFS, initializing the queue with all sources simultaneously.