被围绕的区域

题目链接:

涉及知识:深度优先搜索,广度优先搜索,并查集

分析:

题目并不难,该处主要提供两种解法:广搜和并查集。

广搜:

由题意可知,只要与边界上值为 ‘O’ 的点连通的结点均不会被 ‘X’ 围绕,反之该点会被 ‘X’ 围绕,需要将其改为 ‘X’;

因此,一个直观的想法便是首先找出所有与边界上 ‘O’ 相连的结点,并将其进行标记(假设标记为 ‘S’),然后遍历该矩阵,如果某点被标记,那么说明该点与边界相通,不会被围绕,将其改回 ‘O’ ;如果某点为 ‘O’ 说明该节点会被围绕,将其修改为 ‘X’;

那么如何找出与边界上 ‘O’ 结点相连的 ‘O’ 结点呢,我们可以使用广度优先搜索,将所有边界上的 ‘O’ 结点入队,然后依次判断其上、下、左、右结点是否满足条件(是 ‘O’ 结点),满足则标记入队,否则不做处理。

/*
 * @lc app=leetcode.cn id=130 lang=java
 *
 * [130] 被围绕的区域
 */
class Solution {
    private int[][] dir = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
    private int height;
    private int width;
    private char[][] board;

    //记录每一个结点
    public class Node{
        public int x;
        public int y;

        public Node(int x, int y){
            this.x = x;
            this.y = y;
        }
    }

    public void solve(char[][] board) {
        if(board == null || board.length == 0 || board[0].length == 0){
            return;
        }
        this.width = board[0].length;
        this.height = board.length;
        this.board = board;
        
        bfs();

        return;
    }

    //广搜将与边相连的结点进行标记
    public void bfs(){
        Queue<Node> que = new LinkedList<>();

        //上
        for(int i = 0; i < width; ++i){
            if(board[0][i] == 'O'){
                que.offer(new Node(0, i));
            }
        }
        //左
        for(int i = 0; i < height; ++i){
            if(board[i][0] == 'O'){
                que.offer(new Node(i, 0));
            }
        }
        //下
        for(int i = 0; i < width; ++i){
            if(board[height - 1][i] == 'O'){
                que.offer(new Node(height - 1, i));
            }
        }
        //右
        for(int i = 0; i < height; ++i){
            if(board[i][width - 1] == 'O'){
                que.offer(new Node(i, width - 1));
            }
        }

        //标记所有不会被围绕的结点
        while(!que.isEmpty()){
            Node tmp = que.peek();
            que.poll();

            board[tmp.x][tmp.y] = 'S';
            for(int i = 0; i < dir.length; ++i){
                if(Judge(tmp.x + dir[i][0], tmp.y + dir[i][1])){
                    que.offer(new Node(tmp.x + dir[i][0], tmp.y + dir[i][1]));
                }
            }
        }

        //最后判断是否被标记,,进而处理
        for(int i = 0; i < height; ++i){
            for(int j = 0; j < width; ++j){
                if(board[i][j] == 'S'){
                    board[i][j] = 'O';
                } else if(board[i][j] == 'O'){
                    board[i][j] = 'X';
                }
            }
        }

        return;
    }

    public boolean Judge(int x, int y){
        if(x < 0 || x >= height || y < 0 || y >= width || board[x][y] == 'X' || board[x][y] == 'S'){
            return false;
        }
        return true;
    }
}

并查集

并查集常被用来解决连通性问题,本题中我们可以将所有边界上的 ‘O’ 看成连通,将所有的 ‘O’ 结点与其上下左右的 ‘O’ 结点连通,最后判断 ‘O’ 结点是否与边界上的 ‘O’ 相连,如果相连,则不会被围绕,否则会。

在次我们多引进一个结点, 用于将所有边界上的 ‘O’ 连接起来。

class Solution {
    private int row;
    private int col;

    public void solve(char[][] board) {
        if(board == null || board.length == 0 || board[0].length == 0){
            return;
        }

        row = board.length;
        col = board[0].length;
        int help = row * col;
        UnionSet unionSet = new UnionSet(help + 1);

        for(int i = 0; i < row; ++i){
            for(int j = 0; j < col; ++j){
                if(board[i][j] == 'O'){
                    //如果是边界结点,则将其与 help 合并
                    if((i == 0 || j == 0 || i == row - 1 || j == col - 1)){
                        unionSet.Union(help, getId(i, j));
                    } else {
                        //否则与其上下左右的 O 进行合并
                        if(board[i - 1][j] == 'O'){
                            unionSet.Union(getId(i, j), getId(i - 1, j));
                        }
                        if(board[i + 1][j] == 'O'){
                            unionSet.Union(getId(i, j), getId(i + 1, j));
                        }
                        if(board[i][j - 1] == 'O'){
                            unionSet.Union(getId(i, j), getId(i, j - 1));
                        }
                        if(board[i][j + 1] == 'O'){
                            unionSet.Union(getId(i, j), getId(i, j + 1));
                        }
                    }
                }
            }
        }

        for(int i = 0; i < row; ++i){
            for(int j = 0; j < col; ++j){
                if(board[i][j] == 'O' && unionSet.findRoot(help) != unionSet.findRoot(getId(i, j))){
                    board[i][j] = 'X';
                }
            }
        }

        return;
    }

    public int getId(int x, int y){
        return x * col + y;
    }

    /**
     * 并查集
     */
    public class UnionSet{
        public int[] parent;

        //构建一个大小为 num 的并查集
        public UnionSet(int num){
            parent = new int[num];

            for(int i = 0; i < parent.length; ++i){
                parent[i] = i;
            }
        }

        public int findRoot(int root){
            return (parent[root] == root) ? root : (parent[root] = findRoot(parent[root]));
        }

        public void Union(int x, int y){
            int xRoot = findRoot(x);
            int yRoot = findRoot(y);

            if(xRoot != yRoot){
                parent[xRoot] = yRoot;
            }

            return;
        }
    }
}
posted @ 2019-09-05 18:09  telankesi  阅读(413)  评论(0编辑  收藏  举报