题目: 给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。
三种解法
-
并查集
/**
* @param {character[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
class UnionFind {
constructor (n) {
// 记录连通分量
this.num = n;
// 节点 x 的节点是 parent[x] // 父节点指针初始指向自己
this.parent = new Array(n).fill(0).map((val, i) => i);
// 新增一个数组记录树的“重量”,最初每棵树只有一个节点,重量应该初始化 1
this.size = new Array(n).fill(1);
}
// 返回某个节点 x 的根节点
find (x) {
while(this.parent[x] != x) {
x = this.parent[x];
}
return x;
}
union (p, q) {
let rootP = this.find(p);
let rootQ = this.find(q);
if (rootP == rootQ) return;
// 将两棵树合并为一棵, 小树接到大树下面,较平衡
if (this.size[rootP] > this.size[rootQ]) {
this.parent[rootQ] = rootP;
this.size[rootP] += this.size[rootQ];
} else {
this.parent[rootP] = rootQ;
this.size[rootQ] += this.size[rootP];
}
// 两个分量合二为一
this.num--;
}
// 如果节点p和q连通的话,它们一定拥有相同的根节点
isConnected (p, q) {
let rootP = this.find(p);
let rootQ = this.find(q);
return rootP == rootQ;
}
count () {
return this.num;
}
}
var solve = function(board) {
if (!board.length) return;
let m = board.length, n = board[0].length;
let uf = new UnionFind(m * n + 1);
let dummy = m * n;
// 将首列和末列的 O 与 dummy 连通
for (let 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(let 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 是上下左右搜索的常用手法
let d = [
[1, 0],
[0, 1],
[0, -1],
[-1, 0]
];
for(let i = 1; i < m - 1; i++) {
for (let j = 1; j < n -1; j++) {
// 将此 O 与上下左右的 O 连通
if (board[i][j] == 'O') {
for(let k = 0; k < 4; k++) {
let x = i + d[k][0], y = j + d[k][1];
if(board[x][y] == 'O') {
uf.union(x * n + y, i * n +j);
}
}
}
}
}
// 所有不和 dummy 连通的 O,都要被替换
for(let i = 1; i < m -1; i++) {
for(let j = 1; j < n - 1; j++) {
if(!uf.isConnected(dummy, i * n + j)) {
board[i][j] = 'X';
}
}
}
};
-
DFS 思路
var solve = function (board) {
let rows = board.length, cols = board[0].length;
let directions = [
[0, 1],
[0, -1],
[-1, 0],
[1, 0]
];
const dfs = (i, j) => {
if (i < 0 || j < 0 || i >= rows || j >= cols ||
board[i][j] != 'O' || board[i][j] == 'A') {
return;
}
board[i][j] = 'A';
for (let [x, y] of directions) {
dfs(i + x, j +y);
}
}
for (let i = 0; i < cols; i++) {
if (board[0][i] == 'O') dfs(0, i);
if (board[rows - 1][i] == 'O') dfs(rows - 1, i);
}
for (let i = 0; i < rows; i++) {
// 第1列和最后一列
if (board[i][0] == "O") dfs(i, 0);
if (board[i][cols - 1] == "O") dfs(i, cols - 1);
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] == "A") {
board[i][j] = "O";
} else if (board[i][j] == "O") {
board[i][j] = "X";
}
}
}
}
-
BFS思路
var solve = function (board) {
let rows = board.length,
cols = board[0].length;
// 方向数组
let directions = [
[0, 1],
[0, -1],
[-1, 0],
[1, 0],
];
const bfs = (i, j) => {
let queue = [[i, j]];
while (queue.length) {
let size = queue.length;
while (size--) {
let [x, y] = queue.shift();
if (
x < 0 ||
y < 0 ||
x >= rows ||
y >= cols ||
board[x][y] != "O" ||
board[x][y] == "A"
)
continue;
board[x][y] = "A";
for (let [curI, curJ] of directions) {
queue.push([curI + x, curJ + y]);
}
}
}
};
for (let i = 0; i < cols; i++) {
// 第1行和最后一行
if (board[0][i] == "O") bfs(0, i);
if (board[rows - 1][i] == "O") bfs(rows - 1, i);
}
for (let i = 0; i < rows; i++) {
// 第1列和最后一列
if (board[i][0] == "O") bfs(i, 0);
if (board[i][cols - 1] == "O") bfs(i, cols - 1);
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] == "A") {
board[i][j] = "O";
} else if (board[i][j] == "O") {
board[i][j] = "X";
}
}
}
};