317. Shortest Distance from All Buildings
Problem Description
The problem involves finding the optimal location to build a house on a grid where there are empty lands, buildings, and obstacles. The grid contains cells marked as 0
for empty land, 1
for buildings, and 2
for obstacles. The task is to find an empty cell ('0') to build a house such that the total Manhattan Distance from that house to all buildings on the grid is minimized. The Manhattan Distance between two points (p1, p2) = |p2.x - p1.x| + |p2.y - p1.y|
, which allows movement only in the horizontal and vertical directions (no diagonals). If it is not possible to find such a location that reaches all buildings, the function should return -1
.
Of note, we must respect the rules:
- We cannot pass through buildings (
1
). - We cannot pass through obstacles (
2
).
The aim is to find an empty land (0
) with the shortest total distance to all buildings, which signifies the best location for building the house.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: The problem's grid can be interpreted as a graph where each cell is a node and each adjacent (up, down, left, right) pair of walkable cells (not a building or obstacle) are connected.
Is it a tree?
- No: The graph can have multiple buildings and targets (empty spaces), making it a complex network without a hierarchical tree structure.
Is the problem related to directed acyclic graphs (DAGs)?
- No: The problem is about finding the shortest paths in an undirected grid, not analyzing properties typical of DAGs.
Is the problem related to shortest paths?
- Yes: The goal is to find the shortest total distance from all buildings to any reachable empty space.
Is the graph weighted?
- No: Each step from one cell to an adjacent cell has the same cost (usually considered 1), so the graph is unweighted.
Conclusion: The flowchart points us to use Breadth-First Search (BFS) as it is ideal for unweighted shortest path problems. In this specific problem, a multi-source BFS is used starting from all buildings to calculate the minimum distance to empty spaces, efficiently handling the scenario by exploring level-by-level, guaranteeing shortest paths due to the unweighted nature of the grid.
Intuition
To solve this problem, we need to examine the distances between all the possible empty land spots and every building on the grid to determine the total travel distances for each location. This problem can be approached by using the Breadth-First Search (BFS) algorithm.
The BFS algorithm is suitable for finding the shortest path in a grid-based problem since it explores all neighbor vertices at the present depth before moving on to vertices at the next depth level.
Here's the high-level intuition behind the solution:
- Starting at each building, perform a BFS to compute the distance of each reachable empty land space from the building.
- Accumulate the distances and the number of paths reaching every empty land space during the BFS.
- After performing BFS from all buildings, examine each empty land space to find the one with the shortest accumulated distance reached by all buildings.
- If no such empty land space exists that can be reached by all the buildings, return
-1
.
The key insight is that to minimize the total travel distance, one needs to find an empty land space that is reachable by the most number of buildings and has the lowest summed distance to all of them.
Learn more about Breadth-First Search patterns.
Solution Approach
The given Python solution applies a BFS strategy for every building found on the grid to calculate the total distance of each empty land space from the building and keep track of the empty spaces that have been reached.
Here's a step-by-step breakdown of the solution:
-
Grid Initialization: First, we initialize the necessary grids:
cnt
to track the number of buildings that can reach a particular empty land, anddist
to keep the accumulative distance of each empty land from all buildings. -
BFS Setup: We iterate through every cell in the grid. When a building (
1
) is found, we initialize a queue and perform BFS from that building. -
Performing BFS: For each building, we initialize a BFS queue with the building's location, a set
vis
to keep track of the visited cells, and a variabled
set at 0 representing the distance.- For every level (distance from the building) in the BFS, we increment
d
by1
. - We explore the adjacent cells (up, down, right, left) from the current cell in the queue.
- If we encounter an empty land (
0
) that has not been visited yet, we update thedist
andcnt
grids for that land space to account for this building's distance and the fact that another building has reached it. - We also add the empty land to the queue and
vis
set for further BFS exploration on the next levels.
- For every level (distance from the building) in the BFS, we increment
-
Tracking Reachable Empty Lands: After BFS completes for all buildings, we must determine if there's an empty land reachable from all buildings. We loop through every cell again, and for each empty land (
0
), check if the count of buildings (cnt[i][j]
) that can reach it is equal to the total number of buildings. Only such lands are potential candidates for the house. -
Computing the Optimal Location: Among the possible candidates, we choose the one with the minimum total travel distance (
dist[i][j]
), and if there's no such location, we return-1
.
One thing worth pointing out is the use of the infinite (inf
) value to initialize the answer. This allows us to easily update the minimum using the built-in min
function as we encounter new potential spots while ensuring that if none are found, we easily recognize this and return -1
.
Overall, the solution takes advantage of the properties of BFS to calculate distances level by level which ensures that the shortest path to each empty land is found. By doing so from every building and accumulating the results, we reach a comprehensive solution that evaluates all possible house locations effectively.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach with a small example. Consider a 3x3 grid:
0 1 0 0 2 0 0 0 0
In this grid:
0
s represent empty lands.1
represents a building.2
represents an obstacle.
We want to find the position of an empty land (0
) to build a new house such that the total Manhattan Distance to all buildings is minimized.
Following the solution approach:
- Grid Initialization: First, we initialize our
cnt
grid to track the number of buildings that can reach each empty land and ourdist
grid to keep the accumulative distance from all buildings.
cnt grid: dist grid: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-
BFS Setup: We scan the grid cell by cell. When we find the building (at grid[0][1] in this example), we enqueue its position for BFS.
-
Performing BFS:
-
We start the BFS from the building location
(0, 1)
and initialize our distanced
as0
. Our BFS queue initially contains just the building position. -
For each cell in the queue (the BFS level), we increment
d
by1
before enqueueing all reachable adjacent land cells (those marked with0
). -
As we perform BFS, when we encounter an empty land, we update the
dist
andcnt
grids. For example, we can move from the building to its left and right to the empty plots at(0, 0)
and(0, 2)
, with each having a distance of 1.
-
After first step of BFS: cnt grid: dist grid: 1 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0
-
Tracking Reachable Empty Lands: We have completed the BFS for our only building. Now, for every empty land (
0
), we check if the count (cnt[i][j]
) is equal to the total number of buildings (which is1
in this example). -
Computing the Optimal Location: Now we select the empty land with the minimum total travel distance from the
dist
grid.
Both (0, 0) and (0, 2) are candidates since they each have a building count (cnt
) of 1, which is the total number of buildings. We choose the one with the minimum travel distance from dist
grid.
In this example, both have the same distance of 1, so either could be chosen as the optimal location:
Optimal Location: (0, 0) or (0, 2) with a distance of 1.
Had there been multiple buildings, we would perform BFS from each building, accumulate the distances and counts in the dist
and cnt
grids, and then follow the same steps to select the optimal location based on the total distances and the requirement that the location must be reached by all buildings.
Solution Implementation
1from collections import deque
2
3class Solution:
4 def shortestDistance(self, grid):
5 # Initialize rows `m` and columns `n` based on the grid dimensions
6 m, n = len(grid), len(grid[0])
7
8 # A queue for breadth-first search
9 queue = deque()
10
11 # Total number of buildings
12 total_buildings = 0
13
14 # Data structures to keep count of how many buildings each empty land can reach
15 reach_count = [[0] * n for _ in range(m)]
16
17 # Data structures to keep track of total distances from each empty land to all buildings
18 distance_sum = [[0] * n for _ in range(m)]
19
20 # Loop through each cell in the grid
21 for i in range(m):
22 for j in range(n):
23 # If the cell is a building, perform a BFS from this building
24 if grid[i][j] == 1:
25 total_buildings += 1
26 queue.append((i, j))
27 level_distance = 0
28 visited = set()
29 while queue:
30 # Increase the distance level by 1 for each level of BFS
31 level_distance += 1
32
33 # Loop through each cell in the current BFS level
34 for _ in range(len(queue)):
35 r, c = queue.popleft()
36 # Explore the four directions around the current cell
37 for dr, dc in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
38 x, y = r + dr, c + dc
39 # If the next cell is valid, not visited, and is an empty land
40 if 0 <= x < m and 0 <= y < n and grid[x][y] == 0 and (x, y) not visited:
41 # Increment the building reach count and add the distance
42 reach_count[x][y] += 1
43 distance_sum[x][y] += level_distance
44
45 # Add the cell to the queue and mark it as visited
46 queue.append((x, y))
47 visited.add((x, y))
48
49 # Set an initial answer as infinity to find the minimum
50 answer = float('inf')
51
52 # Loop to find the minimum distance of an empty land cell that can reach all buildings
53 for i in range(m):
54 for j in range(n):
55 if grid[i][j] == 0 and reach_count[i][j] == total_buildings:
56 answer = min(answer, distance_sum[i][j])
57
58 # If no cell can reach all buildings, return -1; otherwise, return the minimum distance
59 return -1 if answer == float('inf') else answer
60
1class Solution {
2 public int shortestDistance(int[][] grid) {
3 int rows = grid.length; // Total number of rows in the grid
4 int cols = grid[0].length; // Total number of columns in the grid
5 Deque<int[]> queue = new LinkedList<>(); // Queue to perform BFS
6 int buildings = 0; // Count of buildings in the grid
7 int[][] count = new int[rows][cols]; // Count of buildings each cell can reach
8 int[][] distance = new int[rows][cols]; // Distance from each cell to all buildings
9 int[] directions = {-1, 0, 1, 0, -1}; // Used to explore 4 directions (up, right, down, left)
10
11 // Explore the grid cell by cell
12 for (int i = 0; i < rows; ++i) {
13 for (int j = 0; j < cols; ++j) {
14 // If we find a building, start BFS from here
15 if (grid[i][j] == 1) {
16 buildings++;
17 queue.offer(new int[] {i, j});
18 int depth = 0; // Depth of the BFS, represents the distance from the start
19 boolean[][] visited = new boolean[rows][cols]; // Visited cells to avoid revisiting
20 while (!queue.isEmpty()) {
21 depth++;
22 for (int k = queue.size(); k > 0; --k) {
23 int[] point = queue.poll();
24 for (int l = 0; l < 4; ++l) {
25 int x = point[0] + directions[l];
26 int y = point[1] + directions[l + 1];
27 // Explore the next cell if it's within the grid bounds, is an empty cell,
28 // and hasn't been visited from this building
29 if (x >= 0 && x < rows && y >= 0 && y < cols &&
30 grid[x][y] == 0 && !visited[x][y]) {
31 count[x][y]++; // Increment reachable buildings count
32 distance[x][y] += depth; // Accumulate distance
33 queue.offer(new int[] {x, y}); // Add to queue for further exploration
34 visited[x][y] = true; // Mark as visited
35 }
36 }
37 }
38 }
39 }
40 }
41 }
42
43 int minDistance = Integer.MAX_VALUE; // Initialize minimum distance to maximum possible value
44 // Look for the cell that can reach all buildings with the minimum distance
45 for (int i = 0; i < rows; ++i) {
46 for (int j = 0; j < cols; ++j) {
47 // If it's an empty cell and can reach all buildings, it's a potential answer
48 if (grid[i][j] == 0 && count[i][j] == buildings) {
49 minDistance = Math.min(minDistance, distance[i][j]);
50 }
51 }
52 }
53
54 // If no such cell is found, return -1, otherwise return the minimum distance found
55 return minDistance == Integer.MAX_VALUE ? -1 : minDistance;
56 }
57}
58
1#include <vector>
2#include <queue>
3#include <climits> // For INT_MAX
4
5using namespace std;
6
7class Solution {
8public:
9 int shortestDistance(vector<vector<int>>& grid) {
10 int rows = grid.size();
11 int cols = grid[0].size();
12 vector<vector<int>> count(rows, vector<int>(cols));
13 vector<vector<int>> distanceSum(rows, vector<int>(cols));
14 vector<int> directions = {-1, 0, 1, 0, -1}; // For traversing up, right, down, and left
15 int buildingTotal = 0; // Number of buildings in the grid
16 int minDistance = INT_MAX; // Initialize minimum distance with the maximum value
17 queue<pair<int, int>> queue;
18
19 // Iterate over each cell of the grid
20 for (int i = 0; i < rows; ++i) {
21 for (int j = 0; j < cols; ++j) {
22 // Start a BFS traversal from each building
23 if (grid[i][j] == 1) {
24 buildingTotal++;
25 queue.emplace(i, j);
26 vector<vector<bool>> visited(rows, vector<bool>(cols));
27 int depth = 0; // The distance from the current building
28
29 // BFS to traverse the entire grid from the current building
30 while (!queue.empty()) {
31 depth++;
32 for (int size = queue.size(); size > 0; --size) {
33 auto point = queue.front();
34 queue.pop();
35 // Go through all 4 directions
36 for (int k = 0; k < 4; ++k) {
37 int x = point.first + directions[k];
38 int y = point.second + directions[k + 1];
39 // Check if the new position is within bounds, is empty land, and has not been visited
40 if (x >= 0 && x < rows && y >= 0 && y < cols && grid[x][y] == 0 && !visited[x][y]) {
41 visited[x][y] = true; // Mark the position as visited
42 count[x][y]++; // Increase the count of how many buildings can reach this position
43 distanceSum[x][y] += depth; // Add the distance from the current building
44 queue.emplace(x, y); // Add to the queue for further BFS
45 }
46 }
47 }
48 }
49 }
50 }
51 }
52
53 // Find the minimum distance sum to all buildings
54 for (int i = 0; i < rows; ++i) {
55 for (int j = 0; j < cols; ++j) {
56 // Check if it's possible to reach all buildings from this cell
57 if (grid[i][j] == 0 && count[i][j] == buildingTotal) {
58 minDistance = min(minDistance, distanceSum[i][j]);
59 }
60 }
61 }
62
63 return minDistance == INT_MAX ? -1 : minDistance; // If no such empty land exists, return -1
64 }
65};
66
1const directions: number[] = [-1, 0, 1, 0, -1]; // Used to move up, right, down, and left
2
3function shortestDistance(grid: number[][]): number {
4 const rows = grid.length;
5 const cols = grid[0].length;
6 const count: number[][] = Array.from({ length: rows }, () => new Array(cols).fill(0));
7 const distanceSum: number[][] = Array.from({ length: rows }, () => new Array(cols).fill(0));
8 let buildingTotal: number = 0; // Number of buildings in the grid
9 let minDistance: number = Number.MAX_SAFE_INTEGER; // Initialize minimum distance with a safe large number
10
11 for (let i = 0; i < rows; ++i) {
12 for (let j = 0; j < cols; ++j) {
13 // Perform a BFS traversal from each building
14 if (grid[i][j] === 1) {
15 buildingTotal++;
16 const queue: [number, number][] = [];
17 queue.push([i, j]);
18 let depth: number = 0; // The distance from the current building
19 const visited: boolean[][] = Array.from({ length: rows }, () => new Array(cols).fill(false));
20
21 // BFS to traverse the grid from the current building
22 while (queue.length > 0) {
23 const levelSize: number = queue.length;
24 depth++;
25 for (let q = 0; q < levelSize; ++q) {
26 const point = queue.shift()!;
27 // Iterate through 4 directions
28 for (let k = 0; k < 4; ++k) {
29 const x = point[0] + directions[k];
30 const y = point[1] + directions[k + 1];
31 // Check if the position is within bounds, is empty land and hasn't been visited
32 if (x >= 0 && x < rows && y >= 0 && y < cols && grid[x][y] === 0 && !visited[x][y]) {
33 visited[x][y] = true;
34 count[x][y]++;
35 distanceSum[x][y] += depth;
36 queue.push([x, y]);
37 }
38 }
39 }
40 }
41 }
42 }
43 }
44
45 // Find the minimum distance to all buildings
46 for (let i = 0; i < rows; ++i) {
47 for (let j = 0; j < cols; ++j) {
48 if (grid[i][j] === 0 && count[i][j] === buildingTotal) {
49 minDistance = Math.min(minDistance, distanceSum[i][j]);
50 }
51 }
52 }
53
54 // Return minimum distance or -1 if no such empty land exists that can reach all buildings
55 return minDistance === Number.MAX_SAFE_INTEGER ? -1 : minDistance;
56}
57
Time and Space Complexity
Time Complexity
The time complexity of the provided code is O((m * n) * (m * n))
, where m
is the number of rows and n
is the number of columns in the grid.
Here's the breakdown of the complexity:
- The nested loop to iterate through the grid takes
O(m * n)
. - The BFS algorithm runs in
O(m * n)
for each building found. Since it runs once for each building, and there could be up tom * n
buildings in the worst case, the total complexity for BFS across all buildings would beO((m * n) * (m * n))
.
Space Complexity
The space complexity of the code is O(m * n)
due to the following reasons:
- The
cnt
anddist
grids each takeO(m * n)
space. - The
vis
set and queueq
can each take up toO(m * n)
space in the worst case when all cells are queued or visited. - Constant factors such as the directions array and the integer
total
do not significantly affect the space complexity.
Learn more about how to find time and space complexity quickly using problem constraints.
How does quick sort divide the problem into subproblems?
Recommended Readings
https algomonster s3 us east 2 amazonaws com cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!