方法一:dfs递归遍历
class Solution {
public void solve(char[][] board) {
if (board.length == 0 || board[0].length == 0)
return;
if (board.length < 2 || board[0].length < 2)
return;
// 表示行
int m = board.length;
// 表示列
int n = board[0].length;
// 第一列和最后一列, 把 'O' 变成 '*'
for (int i = 0; i < m; i++) {
if (board[i][0] == 'O')
dfs(board, i, 0);
if (board[i][n-1] == 'O')
dfs(board, i, n-1);
}
// 第一行和最后一行, 把 'O' 变成 '*'
for (int j = 0; j < n; j++) {
if (board[0][j] == 'O')
dfs(board, 0, j);
if (board[m-1][j] == 'O')
dfs(board, m-1, j);
}
// 循环遍历所有点,把 'O' 变成 'X', '*' 变成 'O'
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] == '*')
board[i][j] = 'O';
}
}
}
// 递归遍历将边界 'O' 和内部与之相连的 'O' 变成 '*'
public void dfs(char[][] board, int i, int j) {
// board[i][j] == '*' 说明已经递归遍历过
if (i < 0 || j < 0 || i >= board.length || j >= board[0].length || board[i][j] == 'X' || board[i][j] == '*') {
return;
}
board[i][j] = '*';
// 上
dfs(board, i - 1, j);
// 下
dfs(board, i + 1, j);
// 左
dfs(board, i, j - 1);
// 右
dfs(board, i, j + 1);
}
}
方法二:union-find 并查集 (转自labuladong的算法小抄)
class Solution {
public void solve(char[][] board) {
if (board.length == 0) return;
int m = board.length;
int n = board[0].length;
// 给 dummy 留一个额外位置
UF uf = new UF(m * n + 1);
int dummy = m * n;
// 将首列和末列的 O 与 dummy 连通
for (int i = 0; i < m; i++) {
if (board[i][0] == 'O')
uf.union(i * n, dummy);
if (board[i][n - 1] == 'O')
uf.union(i * n + n - 1, dummy);
}
// 将首行和末行的 O 与 dummy 连通
for (int j = 0; j < n; j++) {
if (board[0][j] == 'O')
uf.union(j, dummy);
if (board[m - 1][j] == 'O')
uf.union(n * (m - 1) + j, dummy);
}
// 方向数组 d 是上下左右搜索的常用手法
int[][] d = new int[][]{{1,0}, {0,1}, {0,-1}, {-1,0}};
for (int i = 1; i < m - 1; i++)
for (int j = 1; j < n - 1; j++)
if (board[i][j] == 'O')
// 将此 O 与上下左右的 O 连通
for (int k = 0; k < 4; k++) {
int x = i + d[k][0];
int y = j + d[k][1];
if (board[x][y] == 'O')
uf.union(x * n + y, i * n + j);
}
// 所有不和 dummy 连通的 O,都要被替换
for (int i = 1; i < m - 1; i++)
for (int j = 1; j < n - 1; j++)
if (!uf.connected(dummy, i * n + j))
board[i][j] = 'X';
}
}
class UF {
// 记录连通分量个数
private int count;
// 存储若干棵树
private int[] parent;
// 记录树的“重量”
private int[] size;
public UF(int n) {
this.count = n;
parent = new int[n];
size = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
size[i] = 1;
}
}
/* 将 p 和 q 连通 */
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ)
return;
// 小树接到大树下面,较平衡
if (size[rootP] > size[rootQ]) {
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
} else {
parent[rootP] = rootQ;
size[rootQ] += size[rootP];
}
count--;
}
/* 判断 p 和 q 是否互相连通 */
public boolean connected(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
// 处于同一棵树上的节点,相互连通
return rootP == rootQ;
}
/* 返回节点 x 的根节点 */
private int find(int x) {
while (parent[x] != x) {
// 进行路径压缩
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
public int count() {
return count;
}
}