01 Matrix - Python Solution and Time Complexity Analysis


Given a matrix consists of 0 and 1, find the distance to 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.

Understanding the Problem

The core challenge of this problem is to efficiently compute the shortest distance from each cell to the nearest cell containing a 0. This problem is significant in various applications such as image processing, pathfinding in grids, and more. A common pitfall is to use a brute-force approach, which can be highly inefficient for larger matrices.

Approach

To solve this problem, we can use a Breadth-First Search (BFS) approach. BFS is ideal for finding the shortest path in an unweighted grid. Here's a step-by-step approach:

Naive Solution

A naive solution would involve iterating over each cell and performing a BFS or DFS to find the nearest 0. This approach is not optimal as it results in a time complexity of O((m*n)^2), where m and n are the dimensions of the matrix.

Optimized Solution

We can optimize the solution by performing a multi-source BFS. The idea is to start the BFS from all 0s simultaneously. This way, we can propagate the distance to all 1s in the matrix efficiently.

Algorithm

Here is a step-by-step breakdown of the optimized algorithm:

  1. Initialize a queue and enqueue all cells containing 0.
  2. Initialize a distance matrix with infinity for all cells except those containing 0 (set to 0).
  3. Perform BFS from all 0s, updating the distance for each cell based on its neighbors.

Code Implementation

import collections

def updateMatrix(matrix):
    # Get the dimensions of the matrix
    rows, cols = len(matrix), len(matrix[0])
    
    # Initialize the distance matrix with infinity
    dist = [[float('inf')] * cols for _ in range(rows)]
    
    # Initialize the queue for BFS
    queue = collections.deque()
    
    # Enqueue all cells containing 0 and set their distance to 0
    for r in range(rows):
        for c in range(cols):
            if matrix[r][c] == 0:
                dist[r][c] = 0
                queue.append((r, c))
    
    # Directions for moving up, down, left, and right
    directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
    
    # Perform BFS
    while queue:
        r, c = queue.popleft()
        
        # Check all four directions
        for dr, dc in directions:
            nr, nc = r + dr, c + dc
            
            # If the new cell is within bounds and we found a shorter path
            if 0 <= nr < rows and 0 <= nc < cols and dist[nr][nc] > dist[r][c] + 1:
                dist[nr][nc] = dist[r][c] + 1
                queue.append((nr, nc))
    
    return dist

Complexity Analysis

The time complexity of the optimized solution is O(m*n) because each cell is enqueued and dequeued at most once. The space complexity is also O(m*n) due to the distance matrix and the queue.

Edge Cases

Potential edge cases include:

  • A matrix with all 0s.
  • A matrix with all 1s except one 0.
  • Single row or single column matrices.

Each of these cases is handled by the BFS approach, ensuring correct distance calculations.

Testing

To test the solution comprehensively, consider the following test cases:

def test_updateMatrix():
    assert updateMatrix([[0,0,0],[0,1,0],[0,0,0]]) == [[0,0,0],[0,1,0],[0,0,0]]
    assert updateMatrix([[0,0,0],[0,1,0],[1,1,1]]) == [[0,0,0],[0,1,0],[1,2,1]]
    assert updateMatrix([[1,1,1],[1,0,1],[1,1,1]]) == [[1,1,1],[1,0,1],[1,1,1]]
    assert updateMatrix([[0]]) == [[0]]
    assert updateMatrix([[1]]) == [[float('inf')]]  # Assuming no 0s in the matrix

test_updateMatrix()

Thinking and Problem-Solving Tips

When approaching such problems, consider the following tips:

  • Understand the problem constraints and edge cases.
  • Think about the most efficient data structures and algorithms (e.g., BFS for shortest path problems).
  • Break down the problem into smaller, manageable parts.
  • Practice similar problems to improve problem-solving skills.

Conclusion

In this blog post, we discussed the 01 Matrix problem, explored a naive and an optimized solution, and provided a detailed explanation of the BFS approach. Understanding and solving such problems is crucial for developing strong algorithmic thinking and problem-solving skills.

Additional Resources

For further reading and practice, consider the following resources: