返回
JS 通关 LeetCode 130:围困区域
前端
2023-10-07 01:30:08
背景
在 LeetCode 130 中,我们面临一道具有挑战性的难题,即确定并填充被“X”包围的“O”区域。这需要敏锐的算法思维和对 JavaScript 语言的熟练运用。
突破障碍:三种高效算法
为了征服这道难题,我们将探索三种独特的算法:
1. 深度优先搜索(DFS):递归与 DFS 队列
思路:
递归 DFS 遍历矩阵,标记“O”区域为“U”,表示未被包围。如果找到与边界相连的“O”,则将其标记为“#”,表示不会被填充。
代码:
const solveDFS = (board) => {
if (!board || board.length === 0) return;
const rows = board.length, cols = board[0].length;
// 标记边界上的 O
for (let i = 0; i < rows; i++) {
markUnbound(board, i, 0);
markUnbound(board, i, cols - 1);
}
for (let j = 0; j < cols; j++) {
markUnbound(board, 0, j);
markUnbound(board, rows - 1, j);
}
// DFS 遍历
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] === 'O') fillSurrounded(board, i, j);
}
}
// 还原标记
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] === 'U') board[i][j] = 'O';
else if (board[i][j] === '#') board[i][j] = 'O';
}
}
};
const markUnbound = (board, i, j) => {
if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] !== 'O') return;
board[i][j] = '#';
};
const fillSurrounded = (board, i, j) => {
if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] !== 'O') return;
board[i][j] = 'U';
fillSurrounded(board, i - 1, j); // 上
fillSurrounded(board, i + 1, j); // 下
fillSurrounded(board, i, j - 1); // 左
fillSurrounded(board, i, j + 1); // 右
};
2. DFS:使用 DFS 队列
思路:
使用队列存储与边界相连的“O”区域,从这些区域出发进行 DFS,标记所有未被包围的“O”为“#”。
代码:
const solveDFSQueue = (board) => {
if (!board || board.length === 0) return;
const rows = board.length, cols = board[0].length;
const queue = [];
// 入队边界上的 O
for (let i = 0; i < rows; i++) {
if (board[i][0] === 'O') queue.push([i, 0]);
if (board[i][cols - 1] === 'O') queue.push([i, cols - 1]);
}
for (let j = 0; j < cols; j++) {
if (board[0][j] === 'O') queue.push([0, j]);
if (board[rows - 1][j] === 'O') queue.push([rows - 1, j]);
}
// DFS
while (queue.length) {
const [i, j] = queue.shift();
if (i < 0 || i >= rows || j < 0 || j >= cols || board[i][j] !== 'O') continue;
board[i][j] = '#';
queue.push([i - 1, j]); // 上
queue.push([i + 1, j]); // 下
queue.push([i, j - 1]); // 左
queue.push([i, j + 1]); // 右
}
// 还原标记
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] === 'U') board[i][j] = 'O';
else if (board[i][j] === '#') board[i][j] = 'O';
}
}
};
3. 广度优先搜索(BFS):使用队列
思路:
使用队列存储与边界相连的“O”区域,从这些区域出发进行 BFS,将未被包围的“O”标记为“#”。
代码:
const solveBFS = (board) => {
if (!board || board.length === 0) return;
const rows = board.length, cols = board[0].length;
const queue = [];
// 入队边界上的 O
for (let i = 0; i < rows; i++) {
if (board[i][0] === 'O') queue.push([i, 0]);
if (board[i][cols - 1] === 'O') queue.push([i, cols - 1]);
}
for (let j = 0; j < cols; j++) {
if (board[0][j] === 'O') queue.push([0, j]);
if (board[rows - 1][j] === 'O') queue.push([rows - 1, j]);
}
// BFS
while (queue.length) {
const [i, j] = queue.shift();
if (i < 0 || i >= rows || j < 0 || j >= cols || board[i][j] !== 'O') continue;
board[i][j] = '#';
queue.push([i - 1, j]); // 上
queue.push([i + 1, j]); // 下
queue.push([i, j - 1]); // 左
queue.push([i, j + 1]); // 右
}
// 还原标记
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] === 'U') board[i][j] = 'O';
else if (board[i][j] === '#') board[i][j] = 'O';
}
}
};
选择最优算法
对于这道难题,三种算法的效率非常接近。以下是一些选择建议:
- 推荐:DFS 队列 ,代码简练、执行效率高。
- 替代方案:DFS 递归 ,递归方式更直观,但可能存在函数调用堆叠限制。
- 备选方案:BFS 队列 ,队列操作更简单,但速度略慢。