130. Surrounded Regions

Description

Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'.

A region is captured by flipping all 'O's into 'X's in that surrounded region.

For example,

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

After running your function, the board should be:

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

Solution

DFS

Surrounded region怎么定义?从某个'O'往四周开始扩散,如果没有遇到边界,这就是一个surrounded region。但是这样做未免有点麻烦,换一个思路想,如果边界上有'O',那么与它联通的'O'们必定不是surrounded,先找出这种位置并标记成一个中间值'*',那么剩下没有被标记的'O'即为所求,flip掉即可。

class Solution {
    public void solve(char[][] board) {
        if (board == null || board.length == 0 || board[0].length == 0) {
            return;
        }
        
        int m = board.length;
        int n = board[0].length;
        // expand from boundry
        for (int i = 0; i < m; ++i) {
            dfsExpand(board, i, 0);
            dfsExpand(board, i, n - 1);
        }
        
        for (int j = 1; j < n - 1; ++j) {
            dfsExpand(board, 0, j);
            dfsExpand(board, m - 1, j);
        }
        
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                } else if (board[i][j] == '*') {
                    board[i][j] = 'O';
                }
            }
        }
    }
    
    public void dfsExpand(char[][] board, int i, int j) {
        if (i < 0 || i == board.length || j < 0 || j == board[0].length 
            || board[i][j] != 'O') {
            return;
        }
        
        board[i][j] = '*';
        dfsExpand(board, i - 1, j);
        dfsExpand(board, i + 1, j);
        dfsExpand(board, i, j - 1);
        dfsExpand(board, i, j + 1);        
    }
}

BFS

同样的思路很容易就能扩展到BFS上。

class Solution {
    public void solve(char[][] board) {
        if (board == null || board.length == 0 || board[0].length == 0) {
            return;
        }
        
        int m = board.length;
        int n = board[0].length;
        Queue<int[]> queue = new LinkedList<>();
        
        for (int i = 0; i < m; ++i) {
            bfsExpand(board, i, 0, queue);
            bfsExpand(board, i, n - 1, queue);
        }
        
        for (int j = 1; j < n - 1; ++j) {
            bfsExpand(board, 0, j, queue);
            bfsExpand(board, m - 1, j, queue);
        }
        
        while (!queue.isEmpty()) {
            int[] pos = queue.poll();
            bfsExpand(board, pos[0] - 1, pos[1], queue);
            bfsExpand(board, pos[0] + 1, pos[1], queue);
            bfsExpand(board, pos[0], pos[1] - 1, queue);
            bfsExpand(board, pos[0], pos[1] + 1, queue);
            
        }
        
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                } else if (board[i][j] == '*') {
                    board[i][j] = 'O';
                }
            }
        }
    }
    
    public void bfsExpand(char[][] board, int i, int j, Queue<int[]> queue) {
        if (i < 0 || i == board.length || j < 0 || j == board[0].length 
            || board[i][j] != 'O') {
            return;
        }
        
        board[i][j] = '*';
        queue.offer(new int[] {i, j});        
    }
}

Union-Find

也可以用union-find的思路来解这道题,数组中的'O'形成了若干个连通区,如果某个连通区包含在数组boundary上的节点,那么这个连通区不需要被flip。

如何判断某个连通区是否包含boundary上的节点呢?有一个很聪明的做法是,将boundary 'O'节点统统连到一个dummy node上,这样只需要判断是否跟dummy node连通即可。

class Solution {
    public static final int[][] DIRECTIONS = {{1, 0}, {0, 1}};
    
    public void solve(char[][] board) {
        if (board == null || board.length == 0 || board[0].length == 0) {
            return;
        }
        
        int m = board.length;
        int n = board[0].length;
        UnionFind uf = new UnionFind(m * n + 1);
        int dummyNode = m * n;
        
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (board[i][j] != 'O') {
                    continue;
                }
                // connect boundary 'O' to the dummy node component
                if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {
                    uf.union(getIndex(i, j, n), dummyNode);    
                }
                
                for (int[] direction : DIRECTIONS) {
                    int x = i + direction[0];
                    int y = j + direction[1];

                    if (isValid(x, y, m, n) && board[x][y] == 'O') {
                        uf.union(getIndex(i, j, n), getIndex(x, y, n));
                    }
                }
            }
        }
        
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                // expect the dummy node component, others should be flipped
                if (!uf.isConnected(getIndex(i, j, n), dummyNode)) {
                    board[i][j] = 'X';
                }
            }
        }
    }
    
    public int getIndex(int i, int j, int cols) {
        return i * cols + j;
    }
    
    public boolean isValid(int i, int j, int m, int n) {
        return i >= 0 && i < m && j >= 0 && j < n;
    }
    
    class UnionFind {
        int[] parent;
        
        public UnionFind(int n) {
            parent = new int[n];
            for (int i = 0; i < n; ++i) {
                parent[i] = i;
            }
        }
        
        public int find(int x) {
            if (parent[x] != x) {
                parent[x] = find(parent[x]);
            }
            return parent[x];
        }
        
        public void union(int x, int y) {
            parent[find(x)] = parent[find(y)];
        }
        
        public boolean isConnected(int x, int y) {
            return find(x) == find(y);
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容