zoukankan      html  css  js  c++  java
  • [LeetCode 130] 围绕区域(Surrounded Regions)

    问题

    给出一个包含'X'和'O'的2D板,捕获所有'X'围绕的区域。

    一个区域被捕获是指翻转被围绕区域中的所有'O'为'X'。

    例如,

    X X X X

    X O O X

    X X O X

    X O X X

    在运行你的函数后,板子将变为:

    X X X X

    X X X X

    X X X X

    X O X X

     

    初始思路

    判断一个O点是否被X围绕,其实就是看从该点出发有没有一条路径能走到二维数组的区域外。在程序语言中,向上下左右移动分别为:[i-1, j], [i+1, j], [i, j-1], [i, j+1]。而找路径的步骤就是从O点出发向附近的O点不断移动的过程。要实现“不断移动”,一种方案是使用递归调用。由于极端情况下四个方向都能移动,在矩阵较大的时候可以想象递归调用栈的层数将会很多。而另一种比较直观的方案就是不断重复扫描二维数组,做出移动的动作,直到没有点可以移动为止。这种方案看起来比较直观,但是还需要解决几个问题:

    1. 如果在移动过程中有走到边界的情况,即表示改点没有被围绕。要怎么要判断边界点?

    可以通过i-1 < 0, i + 1 >= board.size(), j - 1 < 0, j + 1 >= board[0].size()这四个条件,只要某点坐标符合其中一个条件,它就是在边界上的点。

    2. 由于四个方向移动,如何判断已走过的点?

    由于我们只向O点移动,可以在移动到某点后将该点的值修改,如改为'P'。每次扫描都以P点作为移动的起点。最后根据是否找到路径的情况将P改为X或O。

    3. 如何判断没有点可以移动?

    很简单,我们可以在每次扫描前将一个标志设为false。在该次扫描中只要有过移动,将其置为true。扫描完后根据该变量值决定是否继续即可。

    4. 如何找P点?

    一次二重循环肯定是不够的,因为二重循环是不断地向右下移动。而P点可以向左或向上移动。这里我们先采用一个最简单的方案,不断地从最左上角即[0,0]开始找P,直到没有P点可以移动。这样一定可以保证不会有漏掉的P点。但可以看到将会有一个三重循环。

    好了,看起来解决方案已经有了,让我们模拟一下:

    遍历至[1,1],发现O点,将其置为P。开始扫描

                     [1,1]发现P  [1,2]发现P   [2,2]发现P                          没有路径,将P替换成X

                     只能向右     只能向下      无路可走,没有路径,返回

    X X X X      X X X X      X X X X      X X X X                             X X X X

    X P O X      X P P X       X P P X       X P P X                              X X X X

    X X O X      X X O X      X X P X      X X P X                              X X X X

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

    遍历至[3,1],发现O点,将其置为P。开始扫描

                     [3, 1]发现P                    有路径,将P替换回O

                     无路可走,有路径

    X X X X      X X X X                         X X X X

    X X X X      X X X X                         X X X X

    X X X X      X X X X                         X X X X

    X P X X      X P X X                          X O X X

    看来的确能得出例子中的答案。好了,那么开始写代码吧:

      1 class Solution
      2 {
      3 public:
      4     void solve(std::vector<std::vector<char>> &board)
      5     {     
      6         for(int i = 0; i < board.size(); ++i)
      7         {
      8             for(int j = 0; j < board[i].size(); ++j)
      9             {
     10                 if(board[i][j] == 'O')
     11                 {
     12                     if(!FindPath(board, i, j))
     13                     {
     14                         ReplaceBack(board, SURROUNDED);
     15                     }
     16                     else
     17                     {
     18                         ReplaceBack(board, NOT_SURROUNDED);
     19                     }
     20                 }
     21             }
     22         }    
     23     }
     24 private:
     25     enum ReplaceMethod
     26     {
     27         SURROUNDED,
     28         NOT_SURROUNDED
     29     };
     30 
     31     void ReplaceBack(std::vector<std::vector<char>> &board, ReplaceMethod replaceMethod)
     32     {
     33         for(int i = 0; i < board.size(); ++i)
     34         {
     35             for(int j = 0; j < board[i].size(); ++j)
     36             {
     37                 if(board[i][j] == 'P')
     38                 {
     39                     if(replaceMethod == SURROUNDED)
     40                     {
     41                         board[i][j] = 'X';
     42                     }
     43                     else
     44                     {
     45                         board[i][j] = 'O';
     46                     }
     47                 }
     48             }
     49         }
     50     }
     51 
     52 bool FindPath(std::vector<std::vector<char>> &board, int X, int Y)
     53     {
     54         board[X][Y] = 'P';
     55         
     56         bool hasPath = false;
     57         bool canMove = false;
     58         
     59         while(true)
     60         {
     61             canMove = false;
     62             for(int i = 0; i < board.size(); ++i)
     63             {
     64                 for(int j = 0; j < board[i].size(); ++j)
     65                 {
     66                     if(board[i][j] == 'P')
     67                     {
     68                         if(i - 1 < 0)
     69                         {
     70                             hasPath = true;
     71                         }
     72                         else
     73                         {
     74                             if(board[i-1][j] == 'O')
     75                             {
     76                                 board[i-1][j] = 'P';
     77                                 canMove = true;
     78 
     79                             }
     80                         }
     81                         
     82                         if(j - 1 < 0)
     83                         {
     84                             hasPath = true;
     85                         }
     86                         else
     87                         {
     88                             if(board[i][j - 1] == 'O')
     89                             {
     90                                 board[i][j - 1] = 'P';
     91                                 canMove = true;
     92                                 
     93 
     94                             }
     95                         }
     96                         
     97                         if(i + 1 >= board.size())
     98                         {
     99                             hasPath = true;
    100                         }
    101                         else
    102                         {
    103                             if(board[i+1][j] == 'O')
    104                             {
    105                                 board[i+1][j] = 'P';
    106                                 canMove = true;
    107                             }
    108                         }
    109                         
    110                         if(j + 1 >= board[0].size())
    111                         {
    112                             hasPath = true;
    113                         }
    114                         else
    115                         {
    116                             if(board[i][j + 1] == 'O')
    117                             {
    118                                 board[i][j + 1] = 'P';
    119                                 canMove = true;
    120                             }
    121                         }                    
    122                     }                
    123                 }            
    124             }
    125             if(!canMove)
    126             {
    127                 return hasPath;
    128             }
    129         }
    130         return hasPath;
    131     }
    132 
    133 };
    View Code

    提交后Judge Small顺利通过。然后…… Judge Large果然失败了,处理一个大概250*250的输入时超时。

    优化

    存在这么多n重循环,失败还是意料之中的。分析代码可以发现,循环最多的地方就在发现O点之后调用的FindPath函数里,对这个函数的调用和函数自己的实现肯定是优化的地方。

    先来看对它的调用。每次FindPath返回后,如果找到了路径,我们会将所有P点替换回O点。然而后面再发现这些已经被验证有路径的O点时,又会进入FindPath做一遍查找。为了避免这些不必要的查找,可以改为不将P点换回O点,而是换为另一个标志,如'D'。这样做以后,在最后要多做一次对二维数组的遍历来将D点替换回O点。但是在有路径而且路径很长时这1次遍历相比至少重复O点个数次遍历还是划算的。

    再来看FindPath的实现。为了实现简便,我们会不断的对二维数组从头开始遍历直到没有P点可以移动。由于这是一个三重循环,效率可想而知。前面提到过因为遍历二维数组下标是不断增加的,所以遍历只向右下移动。为了处理P点向左或向上移动的情况我们才采取了这种方案。那我们是不是可以通过修改下标的方式来实现:

    如果向上移动,我们期望从本次横坐标-1的横坐标开始继续遍历。由于对横坐标的遍历在外层循环中,我们还需要终止内层循环:

    1 if(board[i-1][j] == 'O')
    2 {
    3     board[i-1][j] = 'P';
    4 
    5         //注意因为for循环本身会对i进行递增操作,需要减2才能达到
    6         //下次循环i从当前i-1开始的效果                            
    7     i -= 2;
    8         break;
    9 }
    View Code

    注意因为for循环本身会对i进行递增操作,需要减2才能达到下次循环i从当前i-1开始的效果。

    如果向左移动,我们期望从本次纵坐标-1的纵坐标开始继续遍历。由于对纵坐标的遍历就在当前内层循环中,直接重新执行循环即可:

    1 if(board[i][j - 1] == 'O')
    2 {
    3     board[i][j - 1] = 'P';
    4                             
    5     j -= 2;
    6         continue;
    7 }
    View Code

    好了,经过这两个改动,循环的次数应该大大减少了。把所有改动集成进来:

      1 class Solution
      2 {
      3 public:
      4     void solve(std::vector<std::vector<char>> &board)
      5     {
      6         
      7         for(int i = 0; i < board.size(); ++i)
      8         {
      9             for(int j = 0; j < board[i].size(); ++j)
     10             {
     11                 if(board[i][j] == 'O')
     12                 {
     13                     if(!FindPath(board, i, j))
     14                     {
     15                         ReplaceBack(board, SURROUNDED);
     16                     }
     17                     else
     18                     {
     19                         ReplaceBack(board, NOT_SURROUNDED);
     20                     }
     21                 }
     22             }
     23         }
     24         
     25         
     26     for(int i = 0; i < board.size(); ++i)
     27         {
     28             for(int j = 0; j < board[i].size(); ++j)
     29             {
     30                 if(board[i][j] == 'D')
     31                 {
     32                     board[i][j] = 'O';
     33                 }
     34             }
     35         }
     36         
     37         
     38     }
     39 private:
     40     enum ReplaceMethod
     41     {
     42         SURROUNDED,
     43         NOT_SURROUNDED
     44     };
     45 
     46     void ReplaceBack(std::vector<std::vector<char>> &board, ReplaceMethod replaceMethod)
     47     {
     48         for(int i = 0; i < board.size(); ++i)
     49         {
     50             for(int j = 0; j < board[i].size(); ++j)
     51             {
     52                 if(board[i][j] == 'P')
     53                 {
     54                     if(replaceMethod == SURROUNDED)
     55                     {
     56                         board[i][j] = 'X';
     57                     }
     58                     else
     59                     {
     60                         board[i][j] = 'D';
     61                     }
     62                 }
     63             }
     64         }
     65     }
     66 
     67     bool FindPath(std::vector<std::vector<char>> &board, int X, int Y)
     68     {
     69                 board[X][Y] = 'P';
     70         
     71         bool hasPath = false;
     72 
     73         for(int i = 0; i < board.size(); ++i)
     74         {
     75             for(int j = 0; j < board[i].size(); ++j)
     76             {
     77                 if(board[i][j] == 'P')
     78                 {
     79                     if(i - 1 < 0)
     80                     {
     81                         hasPath = true;
     82                     }
     83                     else
     84                     {
     85                         if(board[i-1][j] == 'O')
     86                         {
     87                             board[i-1][j] = 'P';
     88                             
     89                             i -= 2;
     90                             break;
     91                         }
     92                     }
     93                     
     94                     if(j - 1 < 0)
     95                     {
     96                         hasPath = true;
     97                     }
     98                     else
     99                     {
    100                         if(board[i][j - 1] == 'O')
    101                         {
    102                             board[i][j - 1] = 'P';
    103                             
    104                             j -= 2;
    105                             continue;
    106                         }
    107                     }
    108                     
    109                     if(i + 1 >= board.size())
    110                     {
    111                         hasPath = true;
    112                     }
    113                     else
    114                     {
    115                         if(board[i+1][j] == 'O')
    116                         {
    117                             board[i+1][j] = 'P';                            
    118                         }
    119                     }
    120                     
    121                     if(j + 1 >= board[0].size())
    122                     {
    123                         hasPath = true;
    124                     }
    125                     else
    126                     {
    127                         if(board[i][j + 1] == 'O')
    128                         {
    129                             board[i][j + 1] = 'P';        
    130                         }
    131                     }    
    132                 }    
    133             }    
    134         }
    135         return hasPath;
    136     }
    137 };
    View Code

    提交再试:

    顺利通过!

  • 相关阅读:
    swift 关于闭包
    swift 多线程的使用
    线上故障解决流程解读
    coredata (转)
    归档
    NSSearchPathForDirectoriesInDomains
    【iOS开发-91】GCD的同步异步串行并行、NSOperation和NSOperationQueue一级用dispatch_once实现单例(转载)
    Objective-C Runtime (Runtime Library)转发
    Mac电脑系统降级(转载)
    UILabel
  • 原文地址:https://www.cnblogs.com/shawnhue/p/leetcode_130.html
Copyright © 2011-2022 走看看