题目描述
让我们一起来玩扫雷游戏!
给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(’1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。
现在给出在所有未挖出的方块中(’M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:
- 如果一个地雷(’M’)被挖出,游戏就结束了- 把它改为 ‘X’。
- 如果一个没有相邻地雷的空方块(’E’)被挖出,修改它为(’B’),并且所有和其相邻的未挖出方块都应该被递归地揭露。
- 如果一个至少与一个地雷相邻的空方块(’E’)被挖出,修改它为数字(’1’到’8’),表示相邻地雷的数量。
- 如果在此次点击中,若无更多方块可被揭露,则返回面板。
例如:
输入:
1 2 3 4
| [['E', 'E', 'E', 'E', 'E'], ['E', 'E', 'M', 'E', 'E'], ['E', 'E', 'E', 'E', 'E'], ['E', 'E', 'E', 'E', 'E']]
|
Click : [3,0]
输出:
1 2 3 4
| [['B', '1', 'E', '1', 'B'], ['B', '1', 'X', '1', 'B'], ['B', '1', '1', '1', 'B'], ['B', 'B', 'B', 'B', 'B']]
|
解法
思路:这道题的逻辑其实比较简单,模拟扫雷的逻辑就可以了,难就难在我一开始没想到用什么方式来遍历😥题还是刷少了,很喜欢官方题解里这句话——整体看来,一次点击过程会从一个位置出发,逐渐向外圈扩散,这就引导我们使用【搜索】的方法实现。
对每个方块,我们有下面两种情况:
- 当前点击的是未挖出的地雷,修改为X即可
- 当前点击的是未挖出的空方块,我们需要对其周围的八个方块进行判断。如果有地雷,需要统计地雷个数,将当前方块修改为数字;否则修改为B
为了代码简洁,我们可以设置两个数组保存当前元素要移动的步数
我们可以使用深度优先搜索(Depth First Search, DFS)或广度优先搜索(Board First Search, BFS)来进行遍历,注意重复搜索的情况。
1. DFS
对于深度优先搜索来说,我们递归地进行元素遍历。而只要遍历的元素不是E,即不是未挖出的空方块,说明其状态被修改过,就不需要遍历了。
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
| class Solution { int[] directR = new int[]{1, -1, 0, 0, 1, -1, -1, 1}; int[] directC = new int[]{0, 0, 1, -1, -1, -1, 1, 1};
public char[][] updateBoard(char[][] board, int[] click) { int x = click[0], y = click[1]; if (board[x][y] == 'M') { board[x][y] = 'X'; } else { dfs(board, x, y); } return board; }
private void dfs(char[][] board, int r, int c) { int count = 0; int tr, tc; for (int i = 0; i < 8; i++) { tr = r + directR[i]; tc = c + directC[i]; if (tr < 0 || tr > board.length - 1 || tc < 0 || tc > board[0].length - 1) continue; if (board[tr][tc] == 'M') { count++; } } if (count > 0) { board[r][c] = (char) (count + '0'); } else { board[r][c] = 'B'; for (int i = 0; i < 8; i++) { tr = r + directR[i]; tc = c + directC[i]; if (tr < 0 || tr > board.length - 1 || tc < 0 || tc > board[0].length - 1 || board[tr][tc] != 'E') continue; dfs(board, tr, tc); } } } }
|
时间复杂度为$O(m \times n)$ 遍历board二维数组
空间复杂度为$O(m \times n)$ 最差递归栈空间存储全部元素
2. BFS
对于广度优先搜索,我们将该节点周围的节点添加到队列中。解决重复的问题是,入列的E需要先改掉标记。
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
| import java.util.Queue; import java.util.LinkedList; class Solution { int[] directR = new int[]{1, -1, 0, 0, 1, -1, -1, 1}; int[] directC = new int[]{0, 0, 1, -1, -1, -1, 1, 1};
public char[][] updateBoard(char[][] board, int[] click) { int x = click[0], y = click[1]; Queue<int[]> queue = new LinkedList<>(); if (board[x][y] == 'M') { board[x][y] = 'X'; } else { queue.offer(new int[]{x, y}); int[] index; int count, tr, tc; while (queue.size() != 0) { index = queue.poll(); count = 0; for (int i = 0; i < 8; i++) { tr = index[0] + directR[i]; tc = index[1] + directC[i]; if (tr < 0 || tr > board.length - 1 || tc < 0 || tc > board[0].length - 1) continue; if (board[tr][tc] == 'M') { count++; } } if (count > 0) { board[index[0]][index[1]] = (char) (count + '0'); } else { board[index[0]][index[1]] = 'B'; for (int i = 0; i < 8; i++) { tr = index[0] + directR[i]; tc = index[1] + directC[i]; if (tr < 0 || tr > board.length - 1 || tc < 0 || tc > board[0].length - 1 || board[tr][tc] != 'E') continue; board[tr][tc] = 'B'; queue.offer(new int[]{tr, tc}); } } } } return board; } }
|
时间复杂度为$O(m \times n)$ 遍历board二维数组
空间复杂度为$O(m \times n)$ 额外的队列空间储存元素
参考