Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell.

The distance between two adjacent cells is 1.

Example 1:

Input:
[[0,0,0],
 [0,1,0],
 [0,0,0]]

Output:
[[0,0,0],
 [0,1,0],
 [0,0,0]]

Example 2:

Input:
[[0,0,0],
 [0,1,0],
 [1,1,1]]

Output:
[[0,0,0],
 [0,1,0],
 [1,2,1]]

Note:

  1. The number of elements of the given matrix will not exceed 10,000.
  2. There are at least one 0 in the given matrix.
  3. The cells are adjacent in only four directions: up, down, left and right.

Solutions

1. DP

  DP,设dp[i][j]为最小距离,从左上角到右下角跑一次,然后从右下角到左上角跑一次即可。

class Solution(object):
    def updateMatrix(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[List[int]]
        """
        if not matrix: return [[]]
        m, n = len(matrix), len(matrix[0])
        dp = [[0x7fffffff if matrix[i][j] != 0 else 0 for j in range(n)] for i in range(m)]
        for i in range(m):
            for j in range(n):
                self.DP(i, j, m, n, dp)
        for i in range(m - 1, -1, -1):
            for j in range(n - 1, -1, -1):
                self.DP(i, j, m, n, dp)
        return dp

    def DP(self, i, j, m, n, dp):
        if i > 0: dp[i][j] = min(dp[i][j], dp[i - 1][j] + 1)
        if j > 0: dp[i][j] = min(dp[i][j], dp[i][j - 1] + 1)
        if i < m - 1: dp[i][j] = min(dp[i][j], dp[i + 1][j] + 1)
        if j < n - 1: dp[i][j] = min(dp[i][j], dp[i][j + 1] + 1)
# Runtime: 1036 ms, faster than 11.29% of Python3 online submissions for 01 Matrix.
# Memory Usage: 16.7 MB, less than 8.33% of Python3 online submissions for 01 Matrix.

2. DFS

class Solution:
    def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
        if not matrix:
            return matrix
        
        self.r, self.c = len(matrix), len(matrix[0])
        self.directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
        
        for i in range(self.r):
            for j in range(self.c):
                if matrix[i][j] == 1 and (not self.has_neighbor_zero(i, j, matrix)):
                    matrix[i][j] = float('inf')
                    
        for i in range(self.r):
            for j in range(self.c):
                if matrix[i][j] == 1:
                    self.dfs(matrix, i, j, -1)
        return matrix
    
    def has_neighbor_zero(self, row, col, matrix):
        for dire in self.directions:
            x, y = row + dire[0], col + dire[1]
            if x >= 0 and x < self.r and y >= 0 and y < self.c and matrix[x][y] == 0:
                return True
        return False
    
    def dfs(self, matrix, row, col, val):
        if row < 0 or row >= self.r or col < 0 or col >= self.c or matrix[row][col] <= val:
            return
        if val > 0:
            matrix[row][col] = val
        
        for dire in self.directions:
            x, y = row + dire[0], col + dire[1]
            self.dfs(matrix, x, y, matrix[row][col] + 1)
# Runtime: 848 ms, faster than 37.41% of Python3 online submissions for 01 Matrix.
# Memory Usage: 16 MB, less than 25.00% of Python3 online submissions for 01 Matrix.

上面这种解法不是很好理解,换一种。

class Solution:
    def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
        if not matrix:
            return matrix
        
        self.r, self.c = len(matrix), len(matrix[0])
        self.directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
        res = [[float('inf') for _ in range(self.c)] for _ in range(self.r)]
                    
        for i in range(self.r):
            for j in range(self.c):
                if matrix[i][j] == 0:
                    self.dfs(matrix, res, i, j, 0)
        return res
    
    def dfs(self, matrix, res, row, col, dist):
        if row < 0 or row >= self.r or col < 0 or col >= self.c or res[row][col] <= dist:
            return
        
        if matrix[row][col] == 0:
            dist = 0
            
        res[row][col] = dist
        
        for dire in self.directions:
            x, y = row + dire[0], col + dire[1]
            self.dfs(matrix, res, x, y, dist + 1)

这个会 TLE,结果还是必须要用第一种方法,先判断是否有临近的 0 才行:

class Solution:
    def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
        if not matrix:
            return matrix
        
        self.r, self.c = len(matrix), len(matrix[0])
        self.directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
        
        for i in range(self.r):
            for j in range(self.c):
                if matrix[i][j] == 1 and not self.has_neighbor_zero(i, j, matrix):
                    matrix[i][j] = float('inf')
                    
        for i in range(self.r):
            for j in range(self.c):
                self.dfs(matrix, i, j)
        return matrix
    
    def has_neighbor_zero(self, row, col, matrix):
        for dire in self.directions:
            x, y = row + dire[0], col + dire[1]
            if x >= 0 and x < self.r and y >= 0 and y < self.c and matrix[x][y] == 0:
                return True
        return False

    def dfs(self, matrix, row, col):
        for dire in self.directions:
            x, y = row + dire[0], col + dire[1]
            if x < 0 or x >= self.r or y < 0 or y >= self.c:
                continue
            
            if matrix[x][y] > matrix[row][col] + 1:
                matrix[x][y] = matrix[row][col] + 1
                self.dfs(matrix, x, y)
# Runtime: 892 ms, faster than 28.14% of Python3 online submissions for 01 Matrix.
# Memory Usage: 16 MB, less than 16.67% of Python3 online submissions for 01 Matrix.

2. BFS

class Solution(object):
    def updateMatrix(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[List[int]]
        """
        if not matrix:
            return matrix
        r, c = len(matrix), len(matrix[0])
        self.directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
        queue = collections.deque()
        for i in range(r):
            for j in range(c):
                if matrix[i][j] == 0:
                    queue.append((i, j))
                else:
                    matrix[i][j] = float('inf')
        
        while queue:
            cur = queue.popleft()
            for dire in self.directions:
                x, y = cur[0] + dire[0], cur[1] + dire[1]
                if x < 0 or x >= r or y < 0 or y >= c or matrix[x][y] <= matrix[cur[0]][cur[1]]:
                    continue
                matrix[x][y] = matrix[cur[0]][cur[1]] + 1
                queue.append((x, y))
        return matrix
# Runtime: 972 ms, faster than 17.35% of Python3 online submissions for 01 Matrix.
# Memory Usage: 16.2 MB, less than 16.67% of Python3 online submissions for 01 Matrix.

References

  1. 542. 01 Matrix