• 博客园logo
  • 会员
  • 周边
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
neverlandly
博客园    首页    新随笔    联系   管理    订阅  订阅

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 }

 

posted @ 2014-10-10 04:01  neverlandly  阅读(545)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3