Thursday, June 15, 2017

130. Surrounded Regions

三刷 06/2022 
Version #4 BFS from surround regions
这个写法坑比较多,但是硬写出来了,不推荐
还是推荐之前比较巧妙的写法
不过这个写法自己硬写出来了还是证明现在implement的能力提高了
看了之前的写法可以通过从border向里面traverse的方法,就很简单
我自己的写法是先bfs一遍如果bfs的过程中遇到了出界就返回false,如果没有遇到出界就返回true然后再bfs一遍进行flip
这里一个bug就是不能一遇到出界就立刻返回,这样会造成一部分点visited而一部分点没有visited,那些没有visited的点就会被后续的bfs traverse到从而被误判成可以flip的点
关键是遇到出界还要继续直到bfs走完再返回false

Time O(MN)
Space O(MN)

Runtime: 4 ms, faster than 34.91% of Java online submissions for Surrounded Regions.
Memory Usage: 44.7 MB, less than 85.16% of Java online submissions for Surrounded Regions.

class Solution {
    private static int[] dx = new int[]{1, 0, -1, 0};
    private static int[] dy = new int[]{0, -1, 0, 1};
    public void solve(char[][] board) {
        if (board == null || board.length == 0 || board[0] == null || board[0].length == 0) {
            return;
        }
        int rows = board.length;
        int cols = board[0].length;
        boolean[][] visited = new boolean[rows][cols];
        for (int y = 1; y < rows - 1; y++) {
            for (int x = 1; x < cols - 1; x++) {
                if (board[y][x] == 'X' || visited[y][x]) {
                    continue;
                }
                if (bfs(board, visited, y, x, false)) {
                    bfs(board, null, y, x, true);
                }
                
            }
        }
    }
    private boolean bfs(char[][] board, boolean[][] visited, int y, int x, boolean flip) {
        boolean result = true;
        Queue<int[]> que = new ArrayDeque<>();
        que.offer(new int[]{y, x});
        if (flip) {
            board[y][x] = 'X';
        } else {
            visited[y][x] = true;
        }
        while (!que.isEmpty()) {
            int[] curr = que.poll();
            for (int i = 0; i < 4; i++) {
                int ny = curr[0] + dy[i];
                int nx = curr[1] + dx[i];
                // If the neighbor is out of the boundary, means current position is at the boundary
                if (ny < 0 || nx < 0 || ny >= board.length || nx >= board[0].length) {
                    result = false;
                    continue;
                }
                if (board[ny][nx] != 'O' || (!flip && visited[ny][nx])) {
                    continue;
                }
                if (flip) {
                    board[ny][nx] = 'X';
                } else {
                    visited[ny][nx] = true;
                }
                que.offer(new int[]{ny, nx});
            }
        }
        return result;
    }
}

[2ND TRY]
Version #2 Union Find
This is much slower than the DFS version, but it is a very good practice of union find
Connect all 'O' on 4 edges (x == 0 || x == cols - 1 || y == 0 || y == row - 1) to a dummy node
Connect all 'O' to its neighbor 'O's
In the end, mark all node connected to dummy node as 'O', otherwise 'X'

Time O(mn)
Space O(mn)

6.93 %
class Solution {
   class UF {
private int[] id;
private int[] size;
public UF(int N) {
id = new int[N];
size = new int[N];
for (int i = 0; i < N; i++) {
id[i] = i;
size[i] = 1;
}
}

private int root(int i) {
while (id[i] != i) {
id[i] = id[id[i]];
i = id[i];
}
return i;
}

private boolean connected(int p, int q) {
return root(p) == root(q);
}

private void union(int p, int q) {
int rootP = root(p);
int rootQ = root(q);
if (rootP == rootQ) {
return;
}
if (size[rootP] < size[rootQ]) {
id[rootP] = rootQ;
size[rootQ] += size[rootP];
} else {
id[rootQ] = rootP;
size[rootP] += size[rootQ];
}
}
}
private int rows;
private int cols;
public void solve(char[][] board) {
if (board == null || board.length == 0 || board[0] == null || board[0].length == 0) {
return;
}
this.rows = board.length;
this.cols = board[0].length;

UF uf = new UF(rows * cols + 1);
int dummyId = rows * cols;
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
if (board[y][x] == 'O') {
                    int currLabel = getLabel(y, x);
                    if (x == 0 || x == cols - 1 || y == 0 || y == rows - 1) {
                        uf.union(currLabel, dummyId);
                    } else {
                        if (board[y - 1][x] == 'O') {
                            uf.union(currLabel, getLabel(y - 1, x));
                        }
                        if (board[y + 1][x]  == 'O') {
                            uf.union(currLabel, getLabel(y + 1, x));
                        }
                        if (board[y][x - 1] == 'O') {
                            uf.union(currLabel, getLabel(y, x - 1));
                        }
                        if (board[y][x + 1] == 'O') {
                            uf.union(currLabel, getLabel(y, x + 1));
                        }
                    }
                }
}
}
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
if (uf.connected(getLabel(y, x), dummyId)) {
board[y][x] = 'O';
} else {
board[y][x] = 'X';
}
}
}
}

private int getLabel(int y, int x) {
return y * cols + x;
}
}




Version #3 DFS
100.00 %
Similar to BFS version, mark un-surrounded area as '#' and flip back in the end
time O(mn) Space O(1)
class Solution {
    public void solve(char[][] board) {
        if (board == null || board.length == 0 || board[0] == null || board[0].length == 0) {
            return;
        }
int rows = board.length;
int cols = board[0].length;

for (int y = 0; y < rows; y++) {
dfs(y, 0, board);
dfs(y, cols - 1, board);
}
for (int x = 0; x < cols; x++) {
dfs(0, x, board);
dfs(rows - 1, x, board);
}
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
if (board[y][x] == '#') {
board[y][x] = 'O';
} else {
board[y][x] = 'X';
}
}
}
}
private int[] dx = new int[]{1, 0, -1, 0};
private int[] dy = new int[]{0, -1, 0, 1};

private void dfs(int y, int x, char[][] board) {
if (board[y][x] == 'O') {
board[y][x] = '#';
} else {
return;
}
for (int i = 0; i < 4; i++) {
int nextY = y + dy[i];
int nextX = x + dx[i];
if (nextY > 0 && nextY < board.length && nextX > 0 && nextX < board[0].length && board[nextY][nextX] == 'O') {
dfs(nextY, nextX, board);
}
}
}
}


[1 ST TRY]
Version #1 BFS
这道题想法比较巧妙一开始没想出来
求Surrounded Regions比较难,但是从四条边界出发求UnSurrounded Regions就比较容易了
先把UnSurrounded Regions标记为‘U’
最后对全部matrix traverse一遍,依然标记为‘O’的证明是Surrounded Regions
public class Solution {
    private int[] dx = {1, 0, -1, 0};
    private int[] dy = {0, 1, 0, -1};
    public void solve(char[][] board) {
        if (board == null || board.length == 0 || board[0] == null || board[0].length == 0) return;
        //for j = 0 and j = board[0].length - 1
        //for every i
        for (int i = 0; i < board.length; i++) {
            if (board[i][0] == 'O') {
                solve(board, new int[]{i, 0});
            }
            if (board[i][board[0].length - 1] == 'O') {
                solve(board, new int[]{i, board[0].length - 1});
            }
        }
        //for i = 0 and i = board.length - 1
        //for every j
        for (int j = 0; j < board[0].length; j++) {
            if (board[0][j] == 'O') {
                solve(board, new int[]{0, j});
            }
            if (board[board.length - 1][j] == 'O') {
                solve(board, new int[]{board.length - 1, j});
            }
        }
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (board[i][j] == 'O') board[i][j] = 'X';
                if (board[i][j] == 'U') board[i][j] = 'O';
            }
        }
     
    }
    //Set unbounded positions to 'U'
    private void solve(char[][] board, int[] startPoint) {
     
        Queue<int[]> que = new LinkedList<>();
        que.add(startPoint);
        board[startPoint[0]][startPoint[1]] = 'U';
        int[] curr;
        int nx, ny;
        while (!que.isEmpty()) {
            curr = que.poll();
            //neighbors
            for (int i = 0; i < 4; i++) {
                nx = curr[0] + dx[i];
                ny = curr[1] + dy[i];
                if (nx >= 0 && nx < board.length && ny >= 0 && ny < board[0].length) {
                 
                    if (board[nx][ny] == 'O') {
                        board[nx][ny] = 'U';
                        que.offer(new int[]{nx, ny});
                    }
                }
            }
        }
    }
}
二刷 BFS
感觉没有一刷写的好
跟一刷的区别是这里提前把所有boarder的0都加到queue里面了, 看了一刷的答案感觉是unnecessary的
Runtime: 2 ms, faster than 58.83% of Java online submissions for Surrounded Regions.
Memory Usage: 40.8 MB, less than 91.47% of Java online submissions for Surrounded Regions.
class Solution {
    private int[] dx = {1, 0, -1, 0};
    private int[] dy = {0, 1, 0, -1};
    public void solve(char[][] board) {
        int m = board.length;
        int n = board[0].length;
        Queue<int[]> que = new LinkedList<>();
        for (int i = 0; i < m; i++) {
            if (board[i][0] == 'O') {
                board[i][0] = '#';
                que.offer(new int[]{i, 0});
            }
            if (board[i][n - 1] == 'O') {
                board[i][n - 1] = '#';
                que.offer(new int[]{i, n - 1});
            };
        }
        for (int j = 1; j < n - 1; j++) {
            if (board[0][j] == 'O') {
                board[0][j] = '#';
                que.offer(new int[]{0, j});
            }
            if (board[m - 1][j] == 'O') {
                board[m - 1][j] = '#';
                que.offer(new int[]{m - 1, j});
            }
        }
        while (!que.isEmpty()) {
            int[] curr = que.poll();
            int x = curr[0], y = curr[1];
           
            for (int i = 0; i < 4; i++) {
                int nx = x + dx[i];
                int ny = y + dy[i];
                if (nx >= 0 && nx < m && ny >= 0 && ny < n && board[nx][ny] == 'O') {
                    board[nx][ny] = '#';
                    que.offer(new int[]{nx, ny});
                }
            }
        }
        for (int x = 0; x < m; x++) {
            for (int y = 0; y < n; y++) {
                if (board[x][y] == '#') {
                    board[x][y] = 'O';
                } else {
                    board[x][y] = 'X';
                }
            }
        }
    }
}

No comments:

Post a Comment