Txing

欢迎来到 | 伽蓝之堂

0%

Q130-被围绕的区域-中等-DFS/BFS

130. 被围绕的区域

Question

给你一个 m x n 的矩阵 board ,由若干字符 'X''O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O''X' 填充。

x x x x x x x x

x 0 0 x x x x x

x x 0 x x x x x

x 0 x x x 0 x x

Example 1:

输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]] 输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]] 解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

Example 2:

输入:board = [["X"]] 输出:[["X"]]

Note:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 200
  • board[i][j]'X''O'

Approach 1: 深度优先搜索

写在前面 本题给定的矩阵中有三种元素:

  • 字母 X;

  • 被字母 X 包围的字母 O;

  • 没有被字母 X 包围的字母 O。

本题要求将所有被字母 X 包围的字母 O都变为字母 X ,但很难判断哪些 O 是被包围的,哪些 O 不是被包围的。

注意到题目解释中提到:任何边界上的 O 都不会被填充为 X。 我们可以想到,所有的不被包围的 O 都直接或间接与边界上的 O 相连。我们可以利用这个性质判断 O 是否在边界上,具体地说:

  • 对于每一个边界上的 O,我们以它为起点,标记所有与它直接或间接相连的字母 O;
  • 最后我们遍历这个矩阵,对于每一个字母:
    • 如果该字母被标记过,则该字母为没有被字母 X 包围的字母 O,我们将其还原为字母 O;
    • 如果该字母没有被标记过,则该字母为被字母 X 包围的字母 O,我们将其修改为字母 X。

深度优先搜索:

我们可以使用深度优先搜索实现标记操作。在下面的代码中,我们把标记过的字母 O 修改为字母 A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Solution:
def solve(self, board) -> None:
"""
Do not return anything, modify board in-place instead.
"""
if not board:
return

n, m = len(board), len(board[0])

def dfs(x, y):
# 如果board[x][y]不是位于矩阵边框,且字符为'x',则返回为空
if not 0 <= x < n or not 0 <= y < m or board[x][y] != 'O':
return
# 对于位于矩阵边框,且字符为'0'的元素
board[x][y] = "A"
dfs(x + 1, y)
dfs(x - 1, y)
dfs(x, y + 1)
dfs(x, y - 1)

# 检测边框
for i in range(n):
dfs(i, 0)
dfs(i, m - 1)
# 检测边框
for i in range(m - 1):
dfs(0, i)
dfs(n - 1, i)

for i in range(n):
for j in range(m):
if board[i][j] == "A":
board[i][j] = "O"
elif board[i][j] == "O":
board[i][j] = "X"



def main():
# [["X"]]
# [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
solution=Solution().solve(board)
print(board)


if __name__ == "__main__":
main()

复杂度分析

  • 时间复杂度,其中 n 和 m 分别为矩阵的行数和列数。深度优先搜索过程中,每一个点至多只会被标记一次。
  • 空间复杂度,其中 n 和 m 分别为矩阵的行数和列数。主要为深度优先搜索的栈的开销。

Approach 2: 广度优先搜索

我们可以使用广度优先搜索实现标记操作。在下面的代码中,我们把标记过的字母 O 修改为字母 A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution:
def solve_BFS(self, board ) -> None:
"""
Do not return anything, modify board in-place instead.
"""
if not board:
return

n, m = len(board), len(board[0])
# 边框有'0'就放入队列
que = collections.deque()
for i in range(n):
if board[i][0] == "O":
que.append((i, 0))
if board[i][m - 1] == "O":
que.append((i, m - 1))
for i in range(m - 1):
if board[0][i] == "O":
que.append((0, i))
if board[n - 1][i] == "O":
que.append((n - 1, i))
# 队列中的每个元素标记为'A'
while que:
x, y = que.popleft()
board[x][y] = "A"
# 检测周围的4个点是否为非边框,且值为'0',如果是,则加入队列
for mx, my in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]:
if 0 <= mx < n and 0 <= my < m and board[mx][my] == "O":
que.append((mx, my))

for i in range(n):
for j in range(m):
if board[i][j] == "A":
board[i][j] = "O"
elif board[i][j] == "O":
board[i][j] = "X"

复杂度分析

  • 时间复杂度,其中 n 和 分别为矩阵的行数和列数。广度优先搜索过程中,每一个点至多只会被标记一次。
  • 空间复杂度,其中 n 和 m 分别为矩阵的行数和列数。主要为广度优先搜索的队列的开销。