https://leetcode.com/problems/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
解题思路:
这题的tag很重要——breadth first search。如何用广度优先搜索?
遍历整个board,遇到一个'O',就去对他进行广度搜索,上下左右扩展。遇到'O',并且没有visited过的,就将它加入到本次BFS的结果集中,并且将它记录为visited。
一次BFS中,如果有一个'O'在board的边缘,本次BFS的全部元素都不能置为'X'。否则,将它们都置为'X'。
本次BFS结束后,寻找下一个'O',并且没有visited过的,重复上面的过程。
直至board全部遍历结束。
public class Solution { public void solve(char[][] board) { if(board.length == 0) { return; } //记录已经遍历过的格子 int[][] visited = new int[board.length][board[0].length]; //queue的成员是一个{i,j}的数组,记录本次BFS的坐标 Queue<int []> queue = new LinkedList<int []>(); for(int i = 0; i < board.length; i++) { for(int j = 0; j < board[0].length; j++) { if(visited[i][j] == 1) { continue; } //一次BFS就要BFS全部,如果本次BFS的全部'O'元素都不碰到board的边缘,就将他们全部置为X //如果有一个碰到board,全部'O'都不能置为X,但是本次BFS也必须全部结束,并且全部'O'标记为visited,下次BFS不再遍历它们 boolean valid = true; //pathList记录本次BFS全部'O'的坐标,仅当valid==true的时候,将它们都置为'X' ArrayList<int []> pathList = new ArrayList<int []>(); if(board[i][j] == 'O') { queue.offer(new int[]{i, j}); pathList.add(new int[]{i, j}); visited[i][j] = 1; while(queue.size() > 0) { int[] index = queue.poll(); if(index[0] == 0 || index[0] == board.length - 1 || index[1] == 0 || index[1] == board[0].length - 1) { valid = false; } if(index[0] > 0 && board[index[0] - 1][index[1]] == 'O' && visited[index[0] - 1][index[1]] == 0) { queue.offer(new int[]{index[0] - 1, index[1]}); pathList.add(new int[]{index[0] - 1, index[1]}); visited[index[0] - 1][index[1]] = 1; } if(index[1] > 0 && board[index[0]][index[1] - 1] == 'O' && visited[index[0]][index[1] - 1] == 0) { queue.offer(new int[]{index[0], index[1] - 1}); pathList.add(new int[]{index[0], index[1] - 1}); visited[index[0]][index[1] - 1] = 1; } if(index[0] < board.length - 1 && board[index[0] + 1][index[1]] == 'O' && visited[index[0] + 1][index[1]] == 0) { queue.offer(new int[]{index[0] + 1, index[1]}); pathList.add(new int[]{index[0] + 1, index[1]}); visited[index[0] + 1][index[1]] = 1; } if(index[1] < board[0].length - 1 && board[index[0]][index[1] + 1] == 'O' && visited[index[0]][index[1] + 1] == 0) { queue.offer(new int[]{index[0] , index[1] + 1}); pathList.add(new int[]{index[0], index[1] + 1}); visited[index[0]][index[1] + 1] = 1; } } //本次BFS结束,并且没有'O'碰到board边缘,就将他们都置为'X' if(valid) { for(int[] index : pathList) { board[index[0]][index[1]] = 'X'; } } } } } } }
这个解法是AC的,但是时间在500多ms,主流的都在350ms,还是有什么耗时的。后来看到大神也遇到同样的问题,优化了一下。
http://fisherlei.blogspot.sg/2013/03/leetcode-surrounded-regions-solution.html
思路是:因为上面的遍历是对于board按顺序,然后判断有没有'O'在board边缘的。其实我们只要从board的边缘的'O'开始BFS就可以了!寻找从他们可以BFS到的'O',这些'O'都不能置为'X',余下的'O'都可以置为'X'。这个思路和上面的想法完全一致,但是流程上却巧妙很多,避免了很多用来记录的变量。
我把BFS的代码拿出来单独写了一个方法,代码如下:
public class Solution { public void solve(char[][] board) { if(board.length == 0) { return; } //记录已经遍历过的格子 int[][] visited = new int[board.length][board[0].length]; //queue的成员是一个{i,j}的数组,记录本次BFS的坐标 Queue<int []> queue = new LinkedList<int []>(); //上边框 for(int i = 0; i < board[0].length; i++) { if(visited[0][i] == 0 && board[0][i] == 'O') { queue.offer(new int[]{0, i}); visited[0][i] = 1; bfs(board, queue, visited); } } //下边框 for(int i = 0; i < board[0].length; i++) { if(visited[board.length - 1][i] == 0 && board[board.length - 1][i] == 'O') { queue.offer(new int[]{board.length - 1, i}); visited[board.length - 1][i] = 1; bfs(board, queue, visited); } } //左边框 for(int i = 0; i < board.length; i++) { if(visited[i][0] == 0 && board[i][0] == 'O') { queue.offer(new int[]{i, 0}); visited[i][0] = 1; bfs(board, queue, visited); } } //右边框 for(int i = 0; i < board.length; i++) { if(visited[i][board[0].length - 1] == 0 && board[i][board[0].length - 1] == 'O') { queue.offer(new int[]{i, board[0].length - 1}); visited[i][board[0].length - 1] = 1; bfs(board, queue, visited); } } //最后,可以从边框的'O'BFS到的'O',都不能置为'X',其余的'O'都置为'X' for(int i = 0; i < board.length; i++) { for(int j = 0; j < board[0].length; j++) { if(visited[i][j] == 0 && board[i][j] == 'O') { board[i][j] = 'X'; } } } } public void bfs(char[][] board, Queue<int []> queue, int[][] visited) { while(queue.size() > 0) { int[] index = queue.poll(); if(index[0] > 0 && board[index[0] - 1][index[1]] == 'O' && visited[index[0] - 1][index[1]] == 0) { queue.offer(new int[]{index[0] - 1, index[1]}); visited[index[0] - 1][index[1]] = 1; } if(index[1] > 0 && board[index[0]][index[1] - 1] == 'O' && visited[index[0]][index[1] - 1] == 0) { queue.offer(new int[]{index[0], index[1] - 1}); visited[index[0]][index[1] - 1] = 1; } if(index[0] < board.length - 1 && board[index[0] + 1][index[1]] == 'O' && visited[index[0] + 1][index[1]] == 0) { queue.offer(new int[]{index[0] + 1, index[1]}); visited[index[0] + 1][index[1]] = 1; } if(index[1] < board[0].length - 1 && board[index[0]][index[1] + 1] == 'O' && visited[index[0]][index[1] + 1] == 0) { queue.offer(new int[]{index[0] , index[1] + 1}); visited[index[0]][index[1] + 1] = 1; } } } }
这样一改,时间减到了350ms。
上面两种解法,每个格子都仅仅遍历一次,然后需要置为'X'的格子再处理一次,时间复杂度是一致的。但是后一种省去了很多判断和空间。
//20181003
同样的方法,重写一遍。主要是提炼出一个helper方法。
class Solution { public void solve(char[][] board) { if (board.length == 0) { return; } int[][] visited = new int[board.length][board[0].length]; for (int i = 0; i < board.length; i ++) { for (int j = 0; j < board[0].length; j++) { if ((i == 0 || j == 0 || i == board.length - 1 || j == board[0].length - 1) && board[i][j] == 'O') { visited[i][j] = 1; bfs(board, new int[]{i, j}, visited); } } } for (int i = 0; i < board.length; i ++) { for (int j = 0; j < board[0].length; j++) { if (board[i][j] == 'O' && visited[i][j] == 0) { board[i][j] = 'X'; } } } return; } public void bfs(char[][] board, int[] start, int[][] visited) { Queue<int[]> queue = new LinkedList<int[]>(); queue.offer(new int[]{start[0], start[1]}); while (queue.size() > 0) { int[] coordinate = queue.poll(); int x = coordinate[0] - 1; int y = coordinate[1]; helper(queue, board, visited, x, y); x = coordinate[0] + 1; y = coordinate[1]; helper(queue, board, visited, x, y); x = coordinate[0]; y = coordinate[1] - 1; helper(queue, board, visited, x, y); x = coordinate[0]; y = coordinate[1] + 1; helper(queue, board, visited, x, y); } } public void helper(Queue<int[]> queue, char[][] board, int[][] visited, int x, int y) { if (x >= 0 && x < board.length && y >= 0 && y < board[0].length && board[x][y] == 'O' && visited[x][y] == 0) { queue.offer(new int[]{x, y}); visited[x][y] = 1; } } }