zoukankan      html  css  js  c++  java
  • 被围绕的区域

    题目链接:

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

    分析:

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

    广搜:

    由题意可知,只要与边界上值为 ‘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;
            }
        }
    }
    
  • 相关阅读:
    字符串作为map的key
    类成员函数模板特化
    函数模板特化
    linux 下第一个Libevent代码学习
    linux下libevent安装
    随笔
    SELECT INTO 和 INSERT INTO SELECT 两种表复制语句
    字符串处理总结之一(C#String类)
    XPath语法 在C#中使用XPath示例
    C#代码实现邮箱验证C#中及一些常用的正则表达式
  • 原文地址:https://www.cnblogs.com/zcxhaha/p/11468785.html
Copyright © 2011-2022 走看看