这是leetcode面试刷题一题多解系列的第7篇,重点学习和理解下广度优先和深度优先算法。
题目
给你一个
m x n
的矩阵board
,由若干字符'X'
和'O'
,找到所有被'X'
围绕的区域,并将这些区域里所有的'O'
用'X'
填充。
来源:力扣(LeetCode)
链接:leetcode.cn/problems/su…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
此题的核心在于:任何边界上的O都不会被填充为X,所以所有不被包围的O都直接或间接与边界上的O相连。所以我们遍历这个数组边界上的O,以他们为起点,标记所有与它直接或间接相连的字母O。最后我们遍历整个矩阵,对于每一个字母,如果被标记过,则将其还原为字母O,如果没有被标记过,我们将其修改为字母X。
题解1---深度优先
深度优先算法是一种用于遍历或搜索树或图的算法。其本质是通过深度优先遍历的方式,从起点开始一直沿着某一条路径搜索到底,直到到达终点或不能继续前进为止,然后回溯到上一个节点,选择另一条路径继续搜索,直到所有路径都被探索完毕。
function solve(board) {
const m = board.length;
const n = board[0].length;
// 定义 DFS 函数
const dfs = (i, j) => {
if (i < 0 || j < 0 || i >= m || j >= n || board[i][j] !== 'O') {
// 如果当前单元格超出边界或不是 'O',则停止 DFS
return;
}
board[i][j] = '#'; // 将当前单元格标记为已访问
dfs(i + 1, j); // 向下进行 DFS
dfs(i - 1, j); // 向上进行 DFS
dfs(i, j + 1); // 向右进行 DFS
dfs(i, j - 1); // 向左进行 DFS
};
// 从所有边界单元格(左、右、上、下)开始 DFS
for (let i = 0; i < m; i++) {
dfs(i, 0); // 从左边界开始 DFS
dfs(i, n - 1); // 从右边界开始 DFS
}
for (let j = 0; j < n; j++) {
dfs(0, j); // 从上边界开始 DFS
dfs(m - 1, j); // 从下边界开始 DFS
}
// 将所有剩余的 'O' 单元格替换为 'X',将 '#' 单元格恢复为 'O'
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (board[i][j] === 'O') {
board[i][j] = 'X';
} else if (board[i][j] === '#') {
board[i][j] = 'O';
}
}
}
}
在这个算法中,该算法的时间复杂度为 O(mn),其中 m 和 n 分别为矩阵的行数和列数。因为该算法遍历了整个矩阵,并且每个单元格最多被访问一次。 该算法的空间复杂度为 O(mn),主要是由 DFS 栈的空间复杂度引起的。在最坏情况下,如果矩阵中的所有单元格都是 'O',那么 DFS 栈的深度将达到 mn,因此空间复杂度为 O(mn)。
题解2---广度优先算法
广度优先算法是一种基于图论的搜索算法,用于遍历或搜索图形或树的数据结构。在广度优先搜索中,从根节点开始,按照从上到下、从左到右的顺序依次遍历所有节点,每次先访问当前节点的所有子节点,然后再访问子节点的子节点,直到遍历完整个图形或树。
var solve = function(board) {
if (!board || board.length === 0) {
return;
}
const m = board.length;
const n = board[0].length;
const queue = [];
// 遍历边缘,将所有与边缘相连的 O 加入队列
for (let i = 0; i < m; i++) {
if (board[i][0] === 'O') {
queue.push([i, 0]);
}
if (board[i][n - 1] === 'O') {
queue.push([i, n - 1]);
}
}
for (let j = 0; j < n; j++) {
if (board[0][j] === 'O') {
queue.push([0, j]);
}
if (board[m - 1][j] === 'O') {
queue.push([m - 1, j]);
}
}
// 将队列中所有的 O 及其相邻的 O 修改为 '#'
while (queue.length > 0) {
const [i, j] = queue.shift();
if (i < 0 || i >= m || j < 0 || j >= n || 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]);
}
// 将矩阵中的 O 修改为 X,将 # 修改为 O
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (board[i][j] === 'O') {
board[i][j] = 'X';
}
if (board[i][j] === '#') {
board[i][j] = 'O';
}
}
}
};
时间复杂度:O(n×m),其中 n 和 m 分别为矩阵的行数和列数。广度优先搜索过程中,每一个点至多只会被标记一次。 空间复杂度:O(n×m),其中 n 和 m 分别为矩阵的行数和列数。
广度优先搜索(BFS)和深度优先搜索(DFS)是两种最基本的图搜索算法。它们的本质区别在于搜索过程中遍历节点的顺序,深度优先搜索可以快速地搜索到图中深度较大的区域,但可能会陷入死循环。而广度优先搜索可以保证找到最短路径,但可能需要搜索整个图才能找到目标节点。选择哪种算法取决于具体的问题和图的结构。
我的更多前端资讯
欢迎大家技术交流 资料分享 摸鱼 求助皆可 —链接