Surrounded Regions

MediumGraphs · Border DFS🧩 Graph DFSLeetCode #130 ↗Updated

Problem

A board of 'X' and 'O' is given. A group of 4-directionally connected 'O's is captured if none of its cells lies on the board's edge; capture it by turning all of its cells into 'X'. Modify the board in-place; groups touching the edge stay as they are.
InputXXXX / XOOX / XXOX / XOXXOutputXXXX / XXXX / XXXX / XOXX
InputXOutputX

1 ≤ m, n ≤ 200  ·  board[i][j] is 'X' or 'O'  ·  modify in-place, return nothing

Try it yourself

Java
class Solution {
    public void solve(char[][] board) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Invert the question: instead of asking which regions are surrounded, find the ones that are not — every 'O' reachable from an edge 'O' survives; everything else gets flipped.
PatternGraph DFS

Instinct Connected regions, all paths, reachability, cloning or 2-colouring a graph or grid: dive from a node, mark it, recurse into unvisited neighbours.

Memory hack Mark before you march: go deep, paint as you go, and back up only when every door is already painted.

Open the Graph DFS guide →

Approaches

Brute ForceBFS from Every 'O' to Look for the Edge

Time O((m·n)2)Space O(m·n)

For each 'O', BFS through its region with a fresh visited array; if the search ever stands on an edge cell the cell is safe, otherwise remember it. Flip the remembered cells at the end so earlier flips can't affect later searches.

Java
public void solve(char[][] board) {
    List<int[]> flip = new ArrayList<>();
    for (int r = 0; r < board.length; r++)
        for (int c = 0; c < board[0].length; c++)
            if (board[r][c] == 'O' && !reachesEdge(board, r, c)) flip.add(new int[]{r, c});
    for (int[] f : flip) board[f[0]][f[1]] = 'X';
}
private boolean reachesEdge(char[][] b, int r0, int c0) {
    int m = b.length, n = b[0].length;
    int[][] dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    boolean[][] seen = new boolean[m][n];
    Deque<int[]> q = new ArrayDeque<>(List.of(new int[]{r0, c0}));
    seen[r0][c0] = true;
    while (!q.isEmpty()) {
        int[] cur = q.poll();
        int r = cur[0], c = cur[1];
        if (r == 0 || c == 0 || r == m - 1 || c == n - 1) return true;
        for (int[] d : dirs) { // inner cell: neighbours are in bounds
            int x = r + d[0], y = c + d[1];
            if (b[x][y] == 'O' && !seen[x][y]) { seen[x][y] = true; q.add(new int[]{x, y}); }
        }
    }
    return false;
}

Direct translation of the rule, but a region of k cells is explored k times.

At a glance

LevelApproachTimeSpace
Brute ForceBFS from Every 'O' to Look for the EdgeO((m·n)2)O(m·n)
BetterUnion-Find with a Virtual “Edge” NodeO(m·n·log(m·n))O(m·n)
OptimalDFS from the Border, then FlipO(m·n)O(m·n) stack
Common pitfalls
  • Flipping an 'O' before knowing if its region reaches the edge.
  • Forgetting to turn '#' back into 'O'.
  • Recursive find without rank can overflow the stack.
Interview tips
  • Lead with: find what is not surrounded.
  • A temp marker '#' replaces a visited grid.
  • Same border-first trick: LC 1020, LC 417.
Pattern
Flood fill from the border → cells are safe iff connected to the boundary.
My notes saved in this browser