← Back to DevBytes

Walls and Gates: Multiple Solutions and Complexity Analysis

Walls and Gates: Multiple Solutions and Complexity Analysis

The Walls and Gates problem is a classic graph traversal challenge frequently encountered in coding interviews and competitive programming. Given a 2D grid representing a building, you must compute the shortest distance from every empty room to the nearest gate. While the problem statement is simple, it admits several distinct algorithmic approaches — each with different trade-offs in time complexity, space complexity, and code clarity. This tutorial walks through the problem, explores multiple solutions, and analyzes their complexities so you can choose the right approach for your use case.

Problem Statement

You are given an m x n grid rooms initialized with three possible values:

Your task is to fill each empty room with the distance to its nearest gate. If no gate is reachable, the value should remain INF. Distance is measured as the number of steps in a four-directional move (up, down, left, right). You must modify the grid in place.

For example, given the input:

INF  -1   0   INF
INF  INF INF  -1
INF  -1  INF  -1
  0  -1  INF  INF

The expected output is:

  3  -1   0    1
  2   2   1   -1
  1  -1   2   -1
  0  -1   3    4

Why It Matters

This problem is more than an interview exercise. It models real-world scenarios such as:

The problem also serves as a gateway to understanding multi-source breadth-first search, a technique that generalizes to problems like "01 Matrix" (LeetCode #542) and "As Far from Land as Possible" (LeetCode #1162).

Solution 1: Multi-Source BFS (Optimal)

The key insight is that instead of running BFS from every empty room (which would be expensive), we can run BFS simultaneously from all gates at once. We enqueue every gate as a starting point with distance 0, then expand outward layer by layer. Because BFS explores nodes in order of increasing distance, the first time we reach an empty room, we have found its shortest distance to any gate.

This is the canonical optimal solution with O(m * n) time and O(m * n) space in the worst case (when the queue holds many cells).

from collections import deque

def wallsAndGates(rooms):
    if not rooms or not rooms[0]:
        return

    m, n = len(rooms), len(rooms[0])
    INF = 2147483647
    queue = deque()

    # Enqueue all gates as simultaneous BFS sources
    for r in range(m):
        for c in range(n):
            if rooms[r][c] == 0:
                queue.append((r, c))

    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

    while queue:
        r, c = queue.popleft()
        for dr, dc in directions:
            nr, nc = r + dr, c + dc
            # Only expand into unvisited empty rooms
            if 0 <= nr < m and 0 <= nc < n and rooms[nr][nc] == INF:
                rooms[nr][nc] = rooms[r][c] + 1
                queue.append((nr, nc))

How It Works

Each gate acts as a wavefront source. The queue processes cells in non-decreasing order of distance. When a cell is dequeued, we attempt to relax its four neighbors. If a neighbor is still INF, it has not been reached by any gate's wavefront yet, so we set its distance and enqueue it. Walls (-1) and already-visited cells are skipped. Because BFS guarantees shortest paths on unweighted grids, no further relaxation is ever needed.

Complexity Analysis

Solution 2: DFS From Each Gate

An alternative is to perform a depth-first search starting from each gate. DFS is less natural for shortest-path problems because it does not explore in order of distance, but we can still make it correct by only updating a room when we find a strictly smaller distance. This approach is concise but typically slower than BFS due to redundant revisits.

def wallsAndGatesDFS(rooms):
    if not rooms or not rooms[0]:
        return

    m, n = len(rooms), len(rooms[0])
    INF = 2147483647
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

    def dfs(r, c, distance):
        # Only proceed if this path offers a shorter distance
        if distance > rooms[r][c]:
            return
        rooms[r][c] = distance
        for dr, dc in directions:
            nr, nc = r + dr, c + dc
            if 0 <= nr < m and 0 <= nc < n and rooms[nr][nc] != -1:
                # Only recurse if we can improve the neighbor
                if distance + 1 < rooms[nr][nc]:
                    dfs(nr, nc, distance + 1)

    for r in range(m):
        for c in range(n):
            if rooms[r][c] == 0:
                dfs(r, c, 0)

Complexity Analysis

DFS is attractive for its brevity, but for large grids or interview settings where you must justify complexity, BFS is the safer choice.

Solution 3: Brute-Force BFS From Each Room

For completeness, consider the naive approach: for every empty room, run a BFS to find the nearest gate. This is the most intuitive but least efficient solution.

from collections import deque

def wallsAndGatesBrute(rooms):
    if not rooms or not rooms[0]:
        return

    m, n = len(rooms), len(rooms[0])
    INF = 2147483647
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

    def nearest_gate(start_r, start_c):
        queue = deque([(start_r, start_c, 0)])
        visited = [[False] * n for _ in range(m)]
        visited[start_r][start_c] = True
        while queue:
            r, c, d = queue.popleft()
            if rooms[r][c] == 0:
                return d
            for dr, dc in directions:
                nr, nc = r + dr, c + dc
                if 0 <= nr < m and 0 <= nc < n \
                        and not visited[nr][nc] and rooms[nr][nc] != -1:
                    visited[nr][nc] = True
                    queue.append((nr, nc, d + 1))
        return INF

    for r in range(m):
        for c in range(n):
            if rooms[r][c] == INF:
                rooms[r][c] = nearest_gate(r, c)

Complexity Analysis

This approach is included mainly to illustrate why the multi-source BFS is so valuable: by reversing the perspective (starting from gates instead of rooms), we collapse O((m * n)^2) work into O(m * n).

Comparing the Approaches

The table below summarizes the trade-offs:

Approach              | Time          | Space     | Notes
--------------------- | ------------- | --------- | ---------------------------
Multi-source BFS      | O(m * n)      | O(m * n)  | Optimal; recommended
DFS from each gate    | ~O((m*n)^2)   | O(m * n)  | Concise; redundant work
Brute-force per room  | O((m * n)^2)  | O(m * n)  | Simplest; slowest

In nearly all cases, multi-source BFS is the right answer. It is asymptotically optimal, easy to implement, and avoids the pitfalls of recursion depth or repeated work.

Best Practices

Conclusion

The Walls and Gates problem is a compact yet rich exercise in graph traversal that rewards careful thinking about where to start the search. By shifting from a per-room BFS to a multi-source BFS launched from all gates simultaneously, we reduce the time complexity from quadratic to linear while keeping the code clean and intuitive. DFS offers a tempting shortcut but pays for it in redundant work, and the brute-force approach serves as a useful baseline for understanding why the optimal solution is so effective. Mastering this problem equips you with the multi-source BFS pattern, a versatile tool that recurs across distance-field, nearest-facility, and wave-propagation problems throughout algorithmic practice and real-world engineering.

— Ad —

Google AdSense will appear here after approval

← Back to all articles