题目链接
题目描述
让我们一起来玩扫雷游戏!
给你一个大小为 m x n
二维字符矩阵 board
,表示扫雷游戏的盘面,其中:
'M'
代表一个 未挖出的 地雷,'E'
代表一个 未挖出的 空方块,'B'
代表没有相邻(上,下,左,右,和所有 4 个对角线)地雷的 已挖出的 空白方块,- 数字(
'1'
到'8'
)表示有多少地雷与这块 已挖出的 方块相邻, 'X'
则表示一个 已挖出的 地雷。
给你一个整数数组 click
,其中 click = [clickr, clickc]
表示在所有 未挖出的 方块('M'
或者 'E'
)中的下一个点击位置(clickr
是行下标,clickc
是列下标)。
根据以下规则,返回相应位置被点击后对应的盘面:
- 如果一个地雷(
'M'
)被挖出,游戏就结束了 - 把它改为'X'
。 - 如果一个 没有相邻地雷 的空方块(
'E'
)被挖出,修改它为('B'
),并且所有和其相邻的 未挖出 方块都应该被递归地揭露。 - 如果一个 至少与一个地雷相邻 的空方块(
'E'
)被挖出,修改它为数字('1'
到'8'
),表示相邻地雷的数量。 - 如果在此次点击中,若无更多方块可被揭露,则返回盘面。
示例 1:
输入: board = [[“E”,”E”,”E”,”E”,”E”],[“E”,”E”,”M”,”E”,”E”],[“E”,”E”,”E”,”E”,”E”],[“E”,”E”,”E”,”E”,”E”]], click = [3,0]
输出: [[“B”,”1”,”E”,”1”,”B”],[“B”,”1”,”M”,”1”,”B”],[“B”,”1”,”1”,”1”,”B”],[“B”,”B”,”B”,”B”,”B”]]
示例 2:
输入: board = [[“B”,”1”,”E”,”1”,”B”],[“B”,”1”,”M”,”1”,”B”],[“B”,”1”,”1”,”1”,”B”],[“B”,”B”,”B”,”B”,”B”]], click = [1,2]
输出: [[“B”,”1”,”E”,”1”,”B”],[“B”,”1”,”X”,”1”,”B”],[“B”,”1”,”1”,”1”,”B”],[“B”,”B”,”B”,”B”,”B”]]
提示:
m == board.length
n == board[i].length
1 <= m, n <= 50
board[i][j]
为'M'
、'E'
、'B'
或数字'1'
到'8'
中的一个click.length == 2
0 <= clickr < m
0 <= clickc < n
board[clickr][clickc]
为'M'
或'E'
解题思路
方法一:DFS + 模拟
由于题目要求你根据规则来展示执行一次点击操作后游戏面板的变化,所以我们只要明确该扫雷游戏的规则,并用代码模拟出来即可。
那我们着眼于题目的规则,会发现总共分两种情况:
- 当前点击的是「未挖出的地雷」,我们将其值改为 X 即可。
- 当前点击的是「未挖出的空方块」,我们需要统计它周围相邻的方块里地雷的数量 cnt(即 M 的数量)。如果 cnt 为零,即执行规则 2,此时需要将其改为 B,且递归地处理周围的八个未挖出的方块,递归终止条件即为规则 4,没有更多方块可被揭露的时候。否则执行规则 3,将其修改为数字即可。
整体看来,一次点击过程会从一个位置出发,逐渐向外圈扩散,所以这引导我们利用「搜索」的方式来实现。这里以深度优先搜索为例:我们定义递归函数 dfs(x, y) 表示当前在 (x,y) 点,执行扫雷规则的情况,我们只要按照上面理出来的情况来进行模拟即可,在 cnt 为零的时候,对当前点相邻的未挖出的方块调用递归函数,否则将其改为数字,结束递归。
class Solution {
int[] dirX = {-1, 0, 1, 1, 1, 0, -1, -1};
int[] dirY = {1, 1, 1, 0, -1, -1, -1, 0};
public char[][] updateBoard(char[][] board, int[] click) {
if(board[click[0]][click[1]] == 'M'){
board[click[0]][click[1]] = 'X';
}else{
dfs(board, click[0], click[1]);
}
return board;
}
private void dfs(char[][] board, int x, int y){
// 被挖过
if(x < 0 || x >= board.length || y < 0 || y >= board[0].length || board[x][y] != 'E'){
return;
}
// 四周是否有炸弹
int cnt = 0;
for(int i = 0; i < 8; ++i){
int tmpX = x + dirX[i];
int tmpY = y + dirY[i];
if(tmpX >= 0 && tmpX < board.length && tmpY >= 0 && tmpY < board[0].length){
if(board[tmpX][tmpY] == 'M'){
++cnt;
}
}
}
// 没炸弹, 按照规则2,修改它为('B'),并且所有和其相邻的 未挖出 方块都应该被递归地揭露。
if(cnt == 0){
board[x][y] = 'B';
for(int i = 0; i < 8; ++i){
int tmpX = x + dirX[i];
int tmpY = y + dirY[i];
if(tmpX >= 0 && tmpX < board.length && tmpY >= 0 && tmpY < board[0].length){
dfs(board, tmpX, tmpY);
}
}
}else{
// 如果一个 至少与一个地雷相邻 的空方块('E')被挖出,修改它为数字('1' 到 '8' ),表示相邻地雷的数量。
board[x][y] = (char)(cnt + '0');
}
}
}
- 时间复杂度 O(nm)
-
方法二:BFS + 模拟
class Solution {
int[] dirX = {-1, 0, 1, 1, 1, 0, -1, -1};
int[] dirY = {1, 1, 1, 0, -1, -1, -1, 0};
public char[][] updateBoard(char[][] board, int[] click) {
if(board[click[0]][click[1]] == 'M'){
board[click[0]][click[1]] = 'X';
return board;
}
int x = click[0], y = click[1];
Deque<int[]> q = new LinkedList<>();
q.offer(new int[]{x, y});
while(!q.isEmpty()){
int cnt = 0;
click = q.poll();
x = click[0];
y = click[1];
// 已经挖过
if(board[x][y] != 'E'){
continue;
}
for(int i = 0; i < 8; ++i){
int tmpX = x + dirX[i];
int tmpY = y + dirY[i];
if(tmpX >= 0 && tmpX < board.length && tmpY >= 0 && tmpY < board[0].length && board[tmpX][tmpY] == 'M'){
++cnt;
}
}
if(cnt > 0){
board[x][y] = (char)(cnt + '0');
}else{
board[x][y] = 'B';
for(int i = 0; i < 8; ++i){
int tmpX = x + dirX[i];
int tmpY = y + dirY[i];
if(tmpX >= 0 && tmpX < board.length && tmpY >= 0 && tmpY < board[0].length && board[tmpX][tmpY] == 'E'){
q.offer(new int[]{tmpX, tmpY});
}
}
}
}
return board;
}
}
时间复杂度 O(nm)
- 空间复杂度 O(nm)