思路1:和Number of Islands类似,但是此题用DFS会栈溢出,因此使用BFS。从四个边缘的元素开始BFS, 遇到 O 改成特殊字符M,最后没有被修改的O就是被X包围的,应当改为X, 并且把M恢复成O即可。 BFS搜索时队列中维护的是所有未展开BFS的 O,队列记录这些 O的位置。
思路2:利用union find算法,将所有边界可达的O union在一起。设置一个根节点oRoot,边界上的所有O同其union上,非边界的O同其上下左右的O进行union。扫描完board后,那些最终没有和oRoot union在一起的O是需要翻转的。实现时特别注意要将oRoot的秩设得足够大,比如设为矩阵元素个数,确保每个oRoot始终是边界可达O集合的根元素。因为在大矩阵情况下,中间可能产生比较大的秩,若oRoot秩比较小,根元素会被替换从而出错。
对比Number of Islands, 这里使用一维数组实现union find内部数据结构更简洁。
// Method 2: Union Find public class Solution { public void solve(char[][] board) { if (board == null || board.length == 0 || board[0].length == 0) return; int rows = board.length, cols = board[0].length; int oRoot = rows * cols; initUnionFind(rows * cols); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (board[i][j] == 'X') continue; int curr = i * cols + j; if (i == 0 || i == rows - 1 || j == 0 || j == cols - 1) { union(curr, oRoot); } else { if (j + 1 < cols && board[i][j + 1] == 'O') union(curr, i * cols + j + 1); if (j - 1 >= 0 && board[i][j - 1] == 'O') union(curr, i * cols + j - 1); if (i + 1 < rows && board[i + 1][j] == 'O') union(curr, (i + 1) * cols + j); if (i - 1 >= 0 && board[i - 1][j] == 'O') union(curr, (i - 1) * cols + j); } } } for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (board[i][j] == 'O' && find(i * cols + j) != oRoot) { board[i][j] = 'X'; } } } } int[] s; int[] rank; private void initUnionFind(int n) { s = new int[n + 1]; rank = new int[n + 1]; for (int i = 0; i <= n; i++) s[i] = i; rank[n] = n + 1; } private int find(int p) { if (s[p] == p) return p; else return s[p] = find(s[p]); } private void union(int p, int q) { int pRoot = find(p), qRoot = find(q); if (pRoot == qRoot) return; if (rank[pRoot] < rank[qRoot]) { s[pRoot] = qRoot; } else { if (rank[pRoot] == rank[qRoot]) rank[pRoot]++; s[qRoot] = pRoot; } } }
// Method 1 BFS public class Solution { public void solve(char[][] board) { if (board == null || board.length == 0 || board[0].length == 0) return; int m = board.length, n = board[0].length; for (int j = 0; j < n; j++) { if (board[0][j] == 'O') bfs(0, j, board); // row 0 if (board[m - 1][j] == 'O') bfs(m - 1, j, board); // last row } for (int i = 0; i < m; i++) { if (board[i][0] == 'O') bfs(i, 0, board); // col 0 if (board[i][n - 1] == 'O') bfs(i, n - 1, board); // last col } for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (board[i][j] == 'O') board[i][j] = 'X'; else if (board[i][j] == 'M') board[i][j] = 'O'; } } } public void bfs(int i, int j, char[][] board) { LinkedList<Integer> queue = new LinkedList<Integer>(); visit(i, j, board, queue); while (!queue.isEmpty()) { int idx = queue.poll(); int r = idx / board[0].length; int c = idx % board[0].length; visit(r - 1, c, board, queue); visit(r + 1, c, board, queue); visit(r, c - 1, board, queue); visit(r, c + 1, board, queue); } } public void visit(int i, int j, char[][] board, LinkedList<Integer> queue) { if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != 'O') return; board[i][j] = 'M'; queue.offer(i * board[0].length + j); } }