离建筑物最近的距离

Submission

运行时间: 1352 ms

内存: 16.3 MB

class Solution:
    def shortestDistance(self, grid: List[List[int]]) -> int:
        directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]
        m, n = len(grid), len(grid[0])
        dist = [[0 for _ in range(n)] for _ in range(m)]
        buildings = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 1:
                    q = deque()
                    q.append([i,j])
                    dis = 1
                    while q:
                        le = len(q)
                        for _ in range(le):
                            x, y = q.popleft()
                            for dx, dy in directions:
                                nx, ny = x + dx, y + dy
                                if 0 <= nx < m and 0 <= ny < n and grid[nx][ny] == buildings:
                                    grid[nx][ny] -= 1
                                    dist[nx][ny] += dis
                                    q.append([nx, ny])
                        dis += 1
                    buildings -= 1
        
        # print(dist)
        res = float('inf')
        for i in range(m):
            for j in range(n):
                if grid[i][j] == buildings:
                    res = min(res, dist[i][j])
        
        return res if res != float('inf') else -1

Explain

该题解采用BFS(广度优先搜索)的方法。首先遍历网格,找到所有建筑物的位置,然后以每个建筑物为起点进行BFS。在BFS过程中,记录每个空地到当前建筑物的距离,并将距离累加到dist数组中。同时,将访问过的空地标记为负数,避免重复访问。最后,遍历dist数组,找到到所有建筑物距离之和最小的空地,返回其距离和。如果没有符合条件的空地,则返回-1。

时间复杂度: O(b*m*n),其中b为建筑物的数量

空间复杂度: O(m*n)

class Solution:
    def shortestDistance(self, grid: List[List[int]]) -> int:
        directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]
        m, n = len(grid), len(grid[0])
        dist = [[0 for _ in range(n)] for _ in range(m)]  # 存储每个空地到所有建筑物的距离之和
        buildings = 0  # 建筑物的数量
        
        # 遍历网格,找到所有建筑物的位置
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 1:
                    q = deque()
                    q.append([i,j])
                    dis = 1
                    
                    # 从当前建筑物开始BFS
                    while q:
                        le = len(q)
                        for _ in range(le):
                            x, y = q.popleft()
                            for dx, dy in directions:
                                nx, ny = x + dx, y + dy
                                # 如果在网格范围内且为未访问过的空地
                                if 0 <= nx < m and 0 <= ny < n and grid[nx][ny] == buildings:
                                    grid[nx][ny] -= 1  # 标记为已访问
                                    dist[nx][ny] += dis  # 累加距离
                                    q.append([nx, ny])
                        dis += 1
                    buildings -= 1
        
        res = float('inf')
        # 遍历dist数组,找到距离和最小的空地
        for i in range(m):
            for j in range(n):
                if grid[i][j] == buildings:
                    res = min(res, dist[i][j])
        
        return res if res != float('inf') else -1

Explore

在BFS过程中,将访问过的空地标记为负数是为了防止同一层或不同层的BFS重复访问同一个空地,从而导致不必要的计算和错误的距离累加。这种标记方式利用了原始网格数组,减少了额外空间的需求,也使得状态的更新(即标记为空地已访问)与检查(是否已访问过)更为直接和高效。相比于使用额外的visited数组来记录访问状态,这种方法节省了空间并简化了代码逻辑。

在BFS过程中,`dis`变量在每完成一层的遍历后增加1。由于BFS按层遍历,每一层的所有节点都与起点的距离相同,并且比上一层每个节点的距离多1。因此,通过这种逐层增加的方式,`dis`能够准确地反映从起点到当前节点的实际距离。这保证了无论何时将一个新节点加入队列,其记录的距离都是正确的。

在算法中,每遍历到一个建筑物,会将当前建筑物的标记值(开始为1)递减(变为0,然后-1,依此类推)。当BFS从一个建筑物开始时,只有那些标记值等于当前建筑物标记的空地才能被访问和更新。因此,每个空地只会与当前正在进行BFS的建筑物关联一次,确保每个空地到不同建筑物的距离只被计算一次。这通过动态地调整grid中的值来防止重复访问和重复计算。

在这个算法中,从哪个建筑物开始BFS并不影响最终结果,因为算法的目标是计算每个空地到所有建筑物的距离总和。算法确保了每个空地都会被每个建筑物访问一次,无论BFS的起始顺序如何。因此,不需要特别考虑从某个特定建筑物开始的问题。所有建筑物最终都会均等地对结果产生影响,确保了计算的公正性和正确性。