青训营X豆包MarsCode - 488 - 来个遍历| 豆包MarsCode AI 刷题
氦嗨氦,从之前的滑滑滑,到前缀和,再到现在的,挑了一个经典的遍历问题,图图走起!
经典的“其实也很经典”,无效表达了属于是
不说俏皮话了,题号 488 - 被围困的陆地单元格问题。(这个数...)
题目原文
小R正在处理一个
m x n大小的二进制矩阵grid,其中0表示海洋单元格,1表示陆地单元格。一个移动是指从一个陆地单元格走到相邻的另一个陆地单元格,或者跨过网格的边界。小R想知道有多少陆地单元格无法通过任意次数的移动离开网格的边界。也就是说,找出所有被海洋或边界包围的陆地单元格。
题目抽象分析
如何破局?
- 一个二进制矩阵——图论,直接连通矩阵(好像叫这个名字?
- 既然图论相关,在定睛一看,那就是遍历啦
- 请出主角,DFS/BFS!
题目类型
图论+DFS,启动!
BFS挖个坑,但我目前好像没刷到别的图论题
整体思路
解决这个问题的关键在于如何识别那些无法离开网格边界的陆地单元格。我们可以通过以下步骤来解决这个问题:
- 从矩阵的边界开始,使用DFS遍历所有与边界相连的陆地单元格,并将它们标记为已访问。
- 在完成边界搜索后,统计所有未被标记的陆地单元格的数量,这些就是无法离开网格边界的单元格。
代码分块解析
重点我们来看看DFS的思路过程:
标记已访问的单元格
- 在DFS过程中,将访问过的陆地单元格标记为
0(这里等价于海洋了,因为其实都没用),未访问的reamin为1。
从边界开始搜索
- 初始化搜索,遍历矩阵的边界,对每个边界上的陆地单元格调用DFS函数。
计算无法离开边界的陆地单元格数量
- 遍历整个矩阵,统计所有未被标记为已访问的陆地单元格数量。
解题步骤
定义DFS函数+初始化搜索
这个为什么要单独写,是因为很重要:DFS通常都是尾递归的形式!
- 实现DFS函数,用于标记与边界相连的陆地单元格。
- 从矩阵的四个边界开始,调用DFS函数。
统计未被标记的陆地单元格
- 遍历矩阵,计算并返回未被标记的陆地单元格数量。
代码部分
def solution(grid: list) -> int:
if not grid or not grid[0]:
return 0
m, n = len(grid), len(grid[0])
count = 0
def dfs(x, y):
if x < 0 or x >= m or y < 0 or y >= n or grid[x][y] == 0:
return
grid[x][y] = 0 # 标记为已访问
dfs(x - 1, y)
dfs(x + 1, y)
dfs(x, y - 1)
dfs(x, y + 1)
# 从边界开始搜索,将与边界相连的陆地单元格标记为已访问
for i in range(m):
dfs(i, 0)
dfs(i, n - 1)
for j in range(n):
dfs(0, j)
dfs(m - 1, j)
# 计算所有未被标记的陆地单元格的数量
for i in range(m):
for j in range(n):
if grid[i][j] == 1:
count += 1
return count
if __name__ == '__main__':
print(solution(grid=[[0, 0, 0, 0], [1, 0, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]]) == 3)
print(solution(grid=[[1, 1, 1, 1], [1, 0, 0, 1], [1, 0, 0, 1], [1, 1, 1, 1]]) == 0)
print(solution(grid=[[0, 0, 1, 0], [1, 0, 1, 0], [0, 0, 1, 0]]) == 0)
复杂度分析
- 时间复杂度:O(m * n),其中 m 和 n 分别是矩阵的行数和列数。每个单元格最多被访问一次。
- 空间复杂度:O(m * n),在最坏情况下,DFS的递归调用栈可能会达到矩阵的大小,肯定就是这个size了。
总结
为何选此题,因为DFS;图论千千万,D/B解一半(不知道在说什么了打卡睡觉)