本文详细解析LeetCode第317题"离建筑物最近的距离",这是一道图论和广度优先搜索的问题。文章提供了基于多源BFS的解法,包含C#、Python、C++三种语言实现,配有详细的算法分析和性能对比。适合想要提升图论算法能力的程序员。
核心知识点: 广度优先搜索、图论、矩阵遍历
难度等级: 困难
推荐人群: 具有图论基础,想要提升算法能力的程序员
你是个房地产开发商,想要选择一片空地 建一栋大楼。你想把这栋大楼够造在一个距离周边设施都比较方便的地方,通过调研,你希望从它出发能到达所有的建筑物,并且该大楼到每个建筑物的距离之和最小。
给你一个由 0、1 和 2 组成的二维网格,其中:
输入:grid = [[1,0,2,0,1],[0,0,0,0,0],[0,0,1,0,0]]
输出:7
解释:
所有建筑物的位置为 (0,0), (0,4), (2,2)
最优位置为 (1,2) ,距离之和为 7
输入:grid = [[1,0]]
输出:1
输入:grid = [[1]]
输出:-1
这道题可以使用多源BFS来解决。我们需要从每个建筑物出发,计算到每个空地的距离。
关键点:
具体步骤:
时间复杂度:O(m * n * k),其中k是建筑物数量
空间复杂度:O(m * n)
步骤 | 当前建筑物 | 当前层级 | 访问的空地 | 更新的距离 |
---|---|---|---|---|
初始状态 | (0,0) | 0 | [] | [] |
第一步 | (0,0) | 1 | [(0,1), (1,0)] | [1,1] |
第二步 | (0,0) | 2 | [(1,1)] | [2] |
第三步 | (0,4) | 1 | [(0,3), (1,4)] | [1,1] |
第四步 | (0,4) | 2 | [(1,3)] | [2] |
位置 | 到建筑物1距离 | 到建筑物2距离 | 到建筑物3距离 | 总距离 |
---|---|---|---|---|
(1,2) | 2 | 2 | 3 | 7 |
(1,1) | 2 | 3 | 4 | 9 |
(1,3) | 3 | 2 | 2 | 7 |
public class Solution {
private int m, n;
private int[][] directions = new int[][] {
new int[] {-1, 0},
new int[] {1, 0},
new int[] {0, -1},
new int[] {0, 1}
};
public int ShortestDistance(int[][] grid) {
m = grid.Length;
n = grid[0].Length;
int buildingCount = 0;
// 记录到每个建筑物的距离之和
int[,] totalDist = new int[m,n];
// 记录能到达的建筑物数量
int[,] reachCount = new int[m,n];
// 统计建筑物数量
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
buildingCount++;
}
}
}
// 从每个建筑物进行BFS
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
BFS(grid, i, j, totalDist, reachCount);
}
}
}
// 找到最小距离
int minDist = int.MaxValue;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0 && reachCount[i,j] == buildingCount) {
minDist = Math.Min(minDist, totalDist[i,j]);
}
}
}
return minDist == int.MaxValue ? -1 : minDist;
}
private void BFS(int[][] grid, int row, int col, int[,] totalDist, int[,] reachCount) {
Queue<(int, int)> queue = new Queue<(int, int)>();
bool[,] visited = new bool[m,n];
int dist = 0;
queue.Enqueue((row, col));
visited[row,col] = true;
while (queue.Count > 0) {
int size = queue.Count;
dist++;
for (int i = 0; i < size; i++) {
var (curRow, curCol) = queue.Dequeue();
foreach (var dir in directions) {
int newRow = curRow + dir[0];
int newCol = curCol + dir[1];
if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n
&& !visited[newRow,newCol] && grid[newRow][newCol] == 0) {
totalDist[newRow,newCol] += dist;
reachCount[newRow,newCol]++;
visited[newRow,newCol] = true;
queue.Enqueue((newRow, newCol));
}
}
}
}
}
}
class Solution:
def shortestDistance(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
directions = [(-1,0), (1,0), (0,-1), (0,1)]
# 统计建筑物数量
building_count = sum(1 for i in range(m) for j in range(n) if grid[i][j] == 1)
# 记录到每个建筑物的距离之和和能到达的建筑物数量
total_dist = [[0] * n for _ in range(m)]
reach_count = [[0] * n for _ in range(m)]
def bfs(row: int, col: int) -> None:
queue = [(row, col)]
visited = [[False] * n for _ in range(m)]
visited[row][col] = True
dist = 0
while queue:
dist += 1
next_queue = []
for cur_row, cur_col in queue:
for dx, dy in directions:
new_row, new_col = cur_row + dx, cur_col + dy
if (0 <= new_row < m and 0 <= new_col < n and
not visited[new_row][new_col] and grid[new_row][new_col] == 0):
total_dist[new_row][new_col] += dist
reach_count[new_row][new_col] += 1
visited[new_row][new_col] = True
next_queue.append((new_row, new_col))
queue = next_queue
# 从每个建筑物进行BFS
for i in range(m):
for j in range(n):
if grid[i][j] == 1:
bfs(i, j)
# 找到最小距离
min_dist = float('inf')
for i in range(m):
for j in range(n):
if grid[i][j] == 0 and reach_count[i][j] == building_count:
min_dist = min(min_dist, total_dist[i][j])
return min_dist if min_dist != float('inf') else -1
class Solution {
private:
vector<vector<int>> directions = {{-1,0}, {1,0}, {0,-1}, {0,1}};
int m, n;
void bfs(vector<vector<int>>& grid, int row, int col,
vector<vector<int>>& totalDist, vector<vector<int>>& reachCount) {
queue<pair<int,int>> q;
vector<vector<bool>> visited(m, vector<bool>(n, false));
int dist = 0;
q.push({row, col});
visited[row][col] = true;
while (!q.empty()) {
int size = q.size();
dist++;
for (int i = 0; i < size; i++) {
auto [curRow, curCol] = q.front();
q.pop();
for (const auto& dir : directions) {
int newRow = curRow + dir[0];
int newCol = curCol + dir[1];
if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n
&& !visited[newRow][newCol] && grid[newRow][newCol] == 0) {
totalDist[newRow][newCol] += dist;
reachCount[newRow][newCol]++;
visited[newRow][newCol] = true;
q.push({newRow, newCol});
}
}
}
}
}
public:
int shortestDistance(vector<vector<int>>& grid) {
m = grid.size();
n = grid[0].size();
int buildingCount = 0;
vector<vector<int>> totalDist(m, vector<int>(n, 0));
vector<vector<int>> reachCount(m, vector<int>(n, 0));
// 统计建筑物数量
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
buildingCount++;
}
}
}
// 从每个建筑物进行BFS
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
bfs(grid, i, j, totalDist, reachCount);
}
}
}
// 找到最小距离
int minDist = INT_MAX;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0 && reachCount[i][j] == buildingCount) {
minDist = min(minDist, totalDist[i][j]);
}
}
}
return minDist == INT_MAX ? -1 : minDist;
}
};
语言 | 执行用时 | 内存消耗 | 特点 |
---|---|---|---|
C# | 156 ms | 42.8 MB | 实现简洁,性能适中 |
Python | 892 ms | 17.2 MB | 代码最简洁,但性能较差 |
C++ | 68 ms | 16.4 MB | 性能最优,内存占用最小 |
解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
---|---|---|---|---|
多源BFS | O(mnk) | O(m*n) | 直观易懂,实现简单 | 当建筑物较多时效率较低 |
动态规划 | O(mnk) | O(m*n) | 可以复用中间结果 | 实现复杂,不易理解 |
算法专题合集 - 查看完整合集
关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新至第317题。
感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。
如果这篇文章对你有帮助,请:
你的支持是我持续分享的动力!
一起进步:算法学习路上不孤单,欢迎一起交流学习!