zoukankan      html  css  js  c++  java
  • Leetcode: Surrounded Regions

    Given a 2D board containing 'X' and '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

    难度:92。这个题目用到的方法是图形学中的一个常用方法:Flood fill算法,其实就是从一个点出发对周围区域进行目标颜色的填充。背后的思想就是把一个矩阵看成一个图的结构,每个点看成结点,而边则是他上下左右的相邻点,然后进行一次广度或者深度优先搜索。

    这道题首先四个边缘上的‘O’点都不是被surrounded的,这是很直接能看出的,麻烦的是与这些边界上的‘O’点毗邻的其他‘O’点,这些点由于跟边缘上的'O'毗邻,所以也米有被‘X’包裹住。所以我们的想法是:把边界上的‘O’点都找出来,对它们做Flood Fill, 把联通的‘O’区域找出来,把这个区域的点统统由‘O’替换为其他字符比如‘$’。这样没有被替换仍旧为‘O’的那些点,就是被‘X’包裹的。这样整体扫描一次,剩下的所有'O'都应该被替换成'X',而'$'那些最终应该是还原成'O'。

    复杂度分析上,我们先对边缘做Flood fill算法,因为只有是'O'才会进行,而且会被替换成'#',所以每个结点改变次数不会超过一次,因而是O(m*n)的复杂度,最后一次遍历同样是O(m*n),所以总的时间复杂度是O(m*n)。

    空间上没懂,看了别人的思路。空间上就是递归栈(深度优先搜索)或者是队列(广度优先搜索)的空间,同时存在的空间占用不会超过O(m+n)(以广度优先搜索为例,每次队列中的结点虽然会往四个方向拓展,但是事实上这些结点会有很多重复,假设从中点出发,可以想象最大的扩展不会超过一个菱形,也就是n/2*2+m/2*2=m+n,所以算法的空间复杂度是O(m+n))

    方法选择当然DFS和BFS都可以,DFS如果用递归来实现,像Flood Fill算法里那样,图形或者矩阵一般很大,递归容易导致堆栈溢出。所以即使用DFS,也要用Stack来写。

    Approach 1: BFS

     1 public class Solution {
     2     int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
     3     
     4     public void solve(char[][] board) {
     5         if(board == null || board.length == 0 || board[0].length == 0) return;
     6         for (int j=0; j<board[0].length; j++) {
     7             if (board[0][j] == 'O') bfs(board, 0, j);
     8             if (board[board.length-1][j] == 'O') bfs(board, board.length-1, j);
     9         }
    10         for (int i=0; i<board.length; i++) {
    11             if (board[i][0] == 'O') bfs(board, i, 0);
    12             if (board[i][board[0].length-1] == 'O') bfs(board, i, board[0].length-1);
    13         }
    14         for (int i=0; i<board.length; i++) {
    15             for (int j=0; j<board[0].length; j++) {
    16                 if (board[i][j] == 'X') continue;
    17                 else if (board[i][j] == '$') board[i][j] = 'O';
    18                 else if (board[i][j] == 'O') board[i][j] = 'X';
    19             }
    20         }
    21     }
    22     
    23     public void bfs(char[][] board, int i, int j) {
    24         Queue<int[]> queue = new LinkedList<>();
    25         queue.offer(new int[]{i, j});
    26         board[i][j] = '$';
    27         while (queue.size() != 0) {
    28             int[] pair = queue.poll();
    29             int row = pair[0];
    30             int col = pair[1];
    31             
    32             for (int[] dir : dirs) {
    33                 int x = row + dir[0];
    34                 int y = col + dir[1];
    35                 if (x >= 0 && x < board.length && y >= 0 && y < board[0].length && board[x][y] == 'O') {
    36                     queue.offer(new int[]{x, y});
    37                     board[x][y] = '$';
    38                 }
    39             }
    40         }
    41     }
    42 }

    1. 注意BFS一般都需要Visited数组,这里访问元素设置为‘$’其实就相当于做了,就不需要额外visited数组了

    2. 注意设置一个元素为visited的位置,应该在首次把元素放入queue里面的时候(enqueue时),而不是把元素从queue里poll出来的时候,后者TLE错误(错误原因是:enqueue就一定会dequeue,enqueue了就表示一定会visit,所以一定要在这时设置visited,否则如果enqueue了却不设置为visited,其它相邻的点访问的时候也会把这个已经enqueue的点再次enqueue,就会重复访问)。 如下例子所示:

    1----2

    |    /

    |  /

    3

    这个graph1,2,3三个点相邻,假设规则是从queue poll出来的时候才设置visited,那么1点visited以后,2,3两个点都enqueue,但并未visited,2点poll的时候,设置2为visited,同时enqueue 3点。这样3点就enqueue了两次!出错了

    3. 这道题设置visited数组比较特殊,完了之后不需要再撤销visited,即不需要把变成“$”的元素变回“O”。因为题目要求就是要保留这种变化

     下面是一段用递归写的DFS算法,一旦input很大就stack overflow了

     1 public class Solution {
     2     public void solve(char[][] board) {
     3         if(board == null || board.length == 0 || board[0].length == 0) return;
     4         for (int j=0; j<board[0].length; j++) {
     5             if (board[0][j] == 'O') dfs(board, 0, j);
     6             if (board[board.length-1][j] == 'O') dfs(board, board.length-1, j);
     7         }
     8         for (int i=0; i<board.length; i++) {
     9             if (board[i][0] == 'O') dfs(board, i, 0);
    10             if (board[i][board[0].length-1] == 'O') dfs(board, i, board[0].length-1);
    11         }
    12         for (int i=0; i<board.length; i++) {
    13             for (int j=0; j<board[0].length; j++) {
    14                 if (board[i][j] == '$') board[i][j] = 'O';
    15                 else if (board[i][j] == 'O') board[i][j] = 'X';
    16             }
    17         }
    18     }
    19     
    20     public void dfs(char[][] board, int i, int j) {
    21         if (i<0 || i>=board.length || j<0 || j>=board[0].length || board[i][j] != 'O') return;
    22         if (board[i][j] == 'O') board[i][j] = '$';
    23         dfs(board, i-1, j);
    24         dfs(board, i+1, j);
    25         dfs(board, i, j-1);
    26         dfs(board, i, j+1);
    27     }
    28 }

    Union Find: Just know this idea, too complex for this problem

     1 public class Solution {
     2     int rows, cols;
     3     
     4     public void solve(char[][] board) {
     5         if(board == null || board.length == 0) return;
     6         
     7         rows = board.length;
     8         cols = board[0].length;
     9         
    10         // last one is dummy, all outer O are connected to this dummy
    11         UnionFind uf = new UnionFind(rows * cols + 1);
    12         int dummyNode = rows * cols;
    13         
    14         for(int i = 0; i < rows; i++) {
    15             for(int j = 0; j < cols; j++) {
    16                 if(board[i][j] == 'O') {
    17                     if(i == 0 || i == rows-1 || j == 0 || j == cols-1) {
    18                         uf.union(node(i,j), dummyNode);
    19                     }
    20                     else {
    21                         if(i > 0 && board[i-1][j] == 'O')  uf.union(node(i,j), node(i-1,j));
    22                         if(i < rows-1 && board[i+1][j] == 'O')  uf.union(node(i,j), node(i+1,j));
    23                         if(j > 0 && board[i][j-1] == 'O')  uf.union(node(i,j), node(i, j-1));
    24                         if(j < cols-1 && board[i][j+1] == 'O')  uf.union(node(i,j), node(i, j+1));
    25                     }
    26                 }
    27             }
    28         }
    29         
    30         for(int i = 0; i < rows; i++) {
    31             for(int j = 0; j < cols; j++) {
    32                 if(uf.isConnected(node(i,j), dummyNode)) {
    33                     board[i][j] = 'O';
    34                 }
    35                 else {
    36                     board[i][j] = 'X';
    37                 }
    38             }
    39         }
    40     }
    41     
    42     int node(int i, int j) {
    43         return i * cols + j;
    44     }
    45 }
    46 
    47 class UnionFind {
    48     int [] parents;
    49     public UnionFind(int totalNodes) {
    50         parents = new int[totalNodes];
    51         for(int i = 0; i < totalNodes; i++) {
    52             parents[i] = i;
    53         }
    54     }
    55     
    56     void union(int node1, int node2) {
    57         int root1 = find(node1);
    58         int root2 = find(node2);
    59         if(root1 != root2) {
    60             parents[root2] = root1;
    61         }
    62     }
    63     
    64     int find(int node) {
    65         while(parents[node] != node) {
    66             parents[node] = parents[parents[node]];
    67             node = parents[node];
    68         }
    69         
    70         return node;
    71     }
    72     
    73     boolean isConnected(int node1, int node2) {
    74         return find(node1) == find(node2);
    75     }
    76 }
  • 相关阅读:
    append 注意事项
    列表与字符串转换
    删除列表元素
    段寄存器
    通用寄存器
    最强大的王爽汇编语言学习环境使用教程
    JavaScript获取输入框内容
    ubuntu16.04中将python3设置为默认
    Django之cookie和session
    postman 安装桌面版
  • 原文地址:https://www.cnblogs.com/EdwardLiu/p/4014864.html
Copyright © 2011-2022 走看看