题目描述
思路解析动画文字版
记住这把尺子:空格先数雷,有雷填数字就刹车,没雷变 B 接着冲。每一帧都在套它。
盘面总览 · 一颗雷藏在 (1,2):这是初始盘面。红色的 M 是雷,在 (1,2);其余灰色的 E 都是还没挖开的空格。我们要演两种点法。
规则一 · 假如点到的是雷:先看简单的一条。假如这次点的正好是 (1,2),翻开一看是 M,一颗雷。
规则一 · 变 X,游戏结束:碰到雷不数邻居、不扩散,直接把它改成 X,游戏立刻结束,返回盘面。这就是规则一,一步到位。
规则二 · 这次点空格 (0,0):重新开一局。这次点左上角 (0,0),它是空格,不是雷,于是从这里开始一次 DFS 揭开。
检查 (0,0) 的邻居:先把 (0,0) 周围在界内的邻居用橙色圈出来,一个一个看是不是雷,红色的才算一颗。
揭开 (0,0) · 数周围 8 格的雷:进入 (0,0)(紫色)。橙色圈出的是它在界内的邻居,挨个看一遍,数雷时只把标红的雷格算进去,这一格数到 0 颗雷。
变 B · 这格没有雷:周围一颗雷都没有,于是把 (0,0) 改成 B 空白(变蓝)。空白格意味着要继续往外揭开。
扩散 · 把没挖的邻居排进递归:(0,0) 是空白,把它周围还没挖的格 (0,1)、(1,0)、(1,1)(橙色)逐个压进递归栈,接着一个个揭开它们。
检查 (0,1) 的邻居:(0,1) 周围在界内的格子共 5 个,屏幕只圈出仍未揭开的 4 个候选格/雷;已经揭开的 (0,0) 不是雷、不影响这次数雷计数。
揭开 (0,1) · 数周围 8 格的雷:轮到 (0,1),同样数它周围 8 格,这一回数到 1 颗雷。
填数字 1 · 到此为止:周围雷数大于 0,所以 (0,1) 直接填成数字 1(变绿)。它像一道墙,挡住洪水,不再往外扩散。
揭开 (1,0) · 数周围 8 格的雷:轮到 (1,0),同样数它周围 8 格,这一回数到 0 颗雷。
变 B · 这格没有雷:(1,0) 周围同样没雷,也变成 B,继续向外冲。
扩散 · 把没挖的邻居排进递归:(1,0) 是空白,把它周围还没挖的格 (1,1)、(2,0)、(2,1)(橙色)逐个压进递归栈,接着一个个揭开它们。
揭开 (1,1) · 数周围 8 格的雷:轮到 (1,1),同样数它周围 8 格,这一回数到 1 颗雷。
填数字 1 · 到此为止:(1,1) 周围也有雷,填成 1 就刹车,这片墙又长出一格。
揭开 (2,0) · 数周围 8 格的雷:轮到 (2,0),同样数它周围 8 格,这一回数到 0 颗雷。
变 B · 这格没有雷:(2,0) 周围同样没雷,也变成 B,继续向外冲。
扩散 · 把没挖的邻居排进递归:(2,0) 是空白,把它周围还没挖的格 (2,1)(橙色)逐个压进递归栈,接着一个个揭开它们。
揭开 (2,1) · 数周围 8 格的雷:轮到 (2,1),同样数它周围 8 格,这一回数到 1 颗雷。
填数字 1 · 到此为止:(2,1) 周围也有雷,填成 1 就刹车,这片墙又长出一格。
揭开完毕 · 共翻开 6 格:DFS 全部返回。最左一列是三个 B 空白,旁边一列是三个数字 1 组成的墙,墙把右侧没必要揭开的格(含那颗雷 M)挡在外面,这就是最终返回的盘面。
边界先想清:单空格变 B、单雷变 X、贴着雷的空格填数字不扩散。
两个高频追问:DFS 与 BFS 揭开结果相同、深盘面用 BFS 防栈溢出;扫自身格无害。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = rightclass ListNode: def __init__(self, val=0, next=None): self.val = val self.next = nextclass Solution: def updateBoard(self, board: List[List[str]], click: List[int]) -> List[List[str]]: def dfs(i: int, j: int): cnt = 0 for x in range(i - 1, i + 2): for y in range(j - 1, j + 2): if 0 <= x < m and 0 <= y < n and board[x][y] == "M": cnt += 1 if cnt: board[i][j] = str(cnt) else: board[i][j] = "B" for x in range(i - 1, i + 2): for y in range(j - 1, j + 2): if 0 <= x < m and 0 <= y < n and board[x][y] == "E": dfs(x, y) m, n = len(board), len(board[0]) i, j = click if board[i][j] == "M": board[i][j] = "X" else: dfs(i, j) return board复杂度
- 时间:O(m·n),每个格子最多被揭开一次,揭开时数周围 8 格是常数工作
- 空间:O(m·n),最坏情况整片空白连成一块,递归栈深度可达格子总数
易错点
面试追问把动画讲成自己的话
追问用 DFS 和 BFS 做这题有区别吗?
追问数周围 8 格时,把自己这一格也算进循环要紧吗?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
数组嵌套
LeetCode 565 · 中等 · 沿着 数组套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题