LeetCode第317题_离建筑物最近的距离

LeetCode 第317题:离建筑物最近的距离

文章摘要

本文详细解析LeetCode第317题"离建筑物最近的距离",这是一道图论和广度优先搜索的问题。文章提供了基于多源BFS的解法,包含C#、Python、C++三种语言实现,配有详细的算法分析和性能对比。适合想要提升图论算法能力的程序员。

核心知识点: 广度优先搜索、图论、矩阵遍历
难度等级: 困难
推荐人群: 具有图论基础,想要提升算法能力的程序员

题目描述

你是个房地产开发商,想要选择一片空地 建一栋大楼。你想把这栋大楼够造在一个距离周边设施都比较方便的地方,通过调研,你希望从它出发能到达所有的建筑物,并且该大楼到每个建筑物的距离之和最小。

给你一个由 0、1 和 2 组成的二维网格,其中:

  • 0 代表你可以自由通过和选择建造的空地
  • 1 代表你无法通过的建筑物
  • 2 代表你无法通过的障碍物

示例

示例 1:

输入: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

示例 2:

输入:grid = [[1,0]]
输出:1

示例 3:

输入:grid = [[1]]
输出:-1

提示

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 50
  • grid[i][j] 是 0, 1 或 2
  • grid 中至少有一栋建筑物

解题思路

方法:多源BFS

这道题可以使用多源BFS来解决。我们需要从每个建筑物出发,计算到每个空地的距离。

关键点:

  • 从每个建筑物开始进行BFS
  • 记录每个空地到所有建筑物的距离之和
  • 记录每个空地能够到达的建筑物数量
  • 最终结果必须能到达所有建筑物

具体步骤:

  1. 统计建筑物总数
  2. 对每个建筑物进行BFS:
    • 计算到每个空地的距离
    • 更新每个空地的总距离
    • 更新每个空地能到达的建筑物数量
  3. 找到能到达所有建筑物且总距离最小的空地

时间复杂度:O(m * n * k),其中k是建筑物数量
空间复杂度:O(m * n)

图解思路

BFS过程分析表

步骤 当前建筑物 当前层级 访问的空地 更新的距离
初始状态 (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

代码实现

C# 实现

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));
                    }
                }
            }
        }
    }
}

Python 实现

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

C++ 实现

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

性能对比

语言 执行用时 内存消耗 特点
C# 156 ms 42.8 MB 实现简洁,性能适中
Python 892 ms 17.2 MB 代码最简洁,但性能较差
C++ 68 ms 16.4 MB 性能最优,内存占用最小

代码亮点

  1. 使用多源BFS高效解决问题
  2. 通过两个矩阵分别记录距离和可达性
  3. 优化剪枝:只统计能到达所有建筑物的位置
  4. 代码结构清晰,变量命名规范

常见错误分析

  1. 忽略了某些空地可能无法到达所有建筑物
  2. BFS过程中的访问标记处理不当
  3. 距离计算错误或重复计算
  4. 没有正确处理边界情况

解法对比

解法 时间复杂度 空间复杂度 优点 缺点
多源BFS O(mnk) O(m*n) 直观易懂,实现简单 当建筑物较多时效率较低
动态规划 O(mnk) O(m*n) 可以复用中间结果 实现复杂,不易理解

相关题目

  • LeetCode 286. 墙与门 - 中等
  • LeetCode 542. 01 矩阵 - 中等
  • LeetCode 994. 腐烂的橘子 - 中等

系列导航

算法专题合集 - 查看完整合集

关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新至第317题。


互动交流

感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。

如果这篇文章对你有帮助,请:

  • 点个赞,让更多人看到这篇文章
  • 收藏文章,方便后续查阅复习
  • 关注作者,获取更多高质量算法题解
  • 评论区留言,分享你的解题思路或提出疑问

你的支持是我持续分享的动力!

一起进步:算法学习路上不孤单,欢迎一起交流学习!

你可能感兴趣的:(算法,leetcode,linux,算法,c#,学习,python,c++)