Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

130. Surrounded Regions - Leetcode Solution

Code Implementation

class Solution:
    def solve(self, board):
        if not board or not board[0]:
            return
        rows, cols = len(board), len(board[0])

        def dfs(r, c):
            if r < 0 or r >= rows or c < 0 or c >= cols or board[r][c] != 'O':
                return
            board[r][c] = 'E'
            dfs(r+1, c)
            dfs(r-1, c)
            dfs(r, c+1)
            dfs(r, c-1)

        # 1. Mark border-connected 'O's
        for i in range(rows):
            dfs(i, 0)
            dfs(i, cols - 1)
        for j in range(cols):
            dfs(0, j)
            dfs(rows - 1, j)

        # 2. Flip all remaining 'O' to 'X', and 'E' back to 'O'
        for i in range(rows):
            for j in range(cols):
                if board[i][j] == 'O':
                    board[i][j] = 'X'
                elif board[i][j] == 'E':
                    board[i][j] = 'O'
      
class Solution {
public:
    void solve(vector<vector<char>>& board) {
        int rows = board.size();
        if (rows == 0) return;
        int cols = board[0].size();

        function<void(int, int)> dfs = [&](int r, int c) {
            if (r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] != 'O')
                return;
            board[r][c] = 'E';
            dfs(r+1, c);
            dfs(r-1, c);
            dfs(r, c+1);
            dfs(r, c-1);
        };

        // 1. Mark border-connected 'O's
        for (int i = 0; i < rows; ++i) {
            dfs(i, 0);
            dfs(i, cols-1);
        }
        for (int j = 0; j < cols; ++j) {
            dfs(0, j);
            dfs(rows-1, j);
        }

        // 2. Flip all remaining 'O' to 'X', and 'E' back to 'O'
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                if (board[i][j] == 'O')
                    board[i][j] = 'X';
                else if (board[i][j] == 'E')
                    board[i][j] = 'O';
            }
        }
    }
};
      
class Solution {
    public void solve(char[][] board) {
        int rows = board.length;
        if (rows == 0) return;
        int cols = board[0].length;

        for (int i = 0; i < rows; i++) {
            dfs(board, i, 0);
            dfs(board, i, cols - 1);
        }
        for (int j = 0; j < cols; j++) {
            dfs(board, 0, j);
            dfs(board, rows - 1, j);
        }

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] == 'O')
                    board[i][j] = 'X';
                else if (board[i][j] == 'E')
                    board[i][j] = 'O';
            }
        }
    }

    private void dfs(char[][] board, int r, int c) {
        int rows = board.length, cols = board[0].length;
        if (r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] != 'O') return;
        board[r][c] = 'E';
        dfs(board, r + 1, c);
        dfs(board, r - 1, c);
        dfs(board, r, c + 1);
        dfs(board, r, c - 1);
    }
}
      
var solve = function(board) {
    if (!board || board.length === 0) return;
    const rows = board.length, cols = board[0].length;

    function dfs(r, c) {
        if (r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] !== 'O') return;
        board[r][c] = 'E';
        dfs(r + 1, c);
        dfs(r - 1, c);
        dfs(r, c + 1);
        dfs(r, c - 1);
    }

    // 1. Mark border-connected 'O's
    for (let i = 0; i < rows; i++) {
        dfs(i, 0);
        dfs(i, cols - 1);
    }
    for (let j = 0; j < cols; j++) {
        dfs(0, j);
        dfs(rows - 1, j);
    }

    // 2. Flip all remaining 'O' to 'X', and 'E' back to 'O'
    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            if (board[i][j] === 'O') {
                board[i][j] = 'X';
            } else if (board[i][j] === 'E') {
                board[i][j] = 'O';
            }
        }
    }
};
      

Problem Description

You are given a 2D board of characters, where each cell is either 'X' or 'O'. The goal is to capture all regions surrounded by 'X'. A region is captured by flipping all 'O's into 'X's in that surrounded region.

A region is considered "surrounded" if it is completely enclosed by 'X' on all sides (up, down, left, right). Any 'O' cell that is connected to the border of the board (directly or indirectly) is not captured.

You must modify the board in-place. There is only one valid solution for each input. Do not use extra space for another board.

Thought Process

At first glance, you might think to check each 'O' and see if it's completely surrounded by 'X's. However, this quickly becomes complicated, especially for larger boards or regions connected to the edge.

Instead, notice that the only 'O's that should not be flipped are those connected to the border. All other 'O's are surrounded and should be flipped. So, rather than searching for surrounded regions, it's easier to mark all border-connected 'O's and leave them alone.

This shifts the problem from "finding surrounded regions" to "finding safe regions" (those connected to the border).

Solution Approach

  • Step 1: Mark border-connected 'O's.
    • Iterate over the border of the board (first and last rows, first and last columns).
    • For every 'O' found on the border, perform a DFS (depth-first search) or BFS (breadth-first search) to mark all connected 'O's (for example, by changing them to a temporary marker like 'E').
  • Step 2: Flip all other 'O's to 'X'.
    • Now, loop through the board again. Any 'O' not marked is surrounded and should be flipped to 'X'.
    • Change the temporary marker (like 'E') back to 'O' to restore the safe regions.

We use DFS for marking because it is easy to implement and does not require extra space for a queue (if recursion stack is allowed). This approach ensures we only visit each cell a constant number of times.

Example Walkthrough

Consider the input board:

X X X X
X O O X
X X O X
X O X X
    
  1. Mark border-connected 'O's:
    • Start with the borders. Only cell (3,1) is an 'O' on the border. Mark it (e.g., change to 'E').
  2. DFS from (3,1):
    • No other 'O's are connected to (3,1), so only (3,1) is marked.
  3. Flip and restore:
    • All 'O's not marked are surrounded. Flip them to 'X'.
    • Change 'E' back to 'O'.

Final board:

X X X X
X X X X
X X X X
X O X X
      

Time and Space Complexity

  • Brute-force approach:
    • Checking each 'O' for being surrounded individually is O((mn)^2) for an m x n board, because each check could traverse the whole board.
  • Optimized approach (DFS marking):
    • Each cell is visited at most twice (once for marking, once for flipping/restoring), so total time is O(mn).
    • Space is O(1) extra (in-place), except for recursion stack which can be up to O(mn) in the worst case (if all cells are 'O').

Summary

The key insight is to focus on which 'O's are not surrounded: those connected to the border. By marking these first and then flipping the rest, we avoid complicated checks and achieve an efficient, elegant solution. This approach is easy to implement and works in-place, making it suitable for interview and real-world scenarios.