《LeetCode 529 扫雷游戏 FloodFill DFS 解法》

一.题目

529. 扫雷游戏 - 力扣(LeetCode)

二.思路讲解

2.1 审题

本题模拟扫雷游戏的点击规则。盘面由五种字符表示:

  • M:未挖出的地雷

  • E:未挖出的空方块

  • B:已挖出的空白方块(周围无地雷)

  • 数字 1~8:已挖出的方块,表示周围地雷个数

  • X:已挖出的地雷

点击规则如下:

  1. 如果点击到 M ,游戏结束,将其改为 X

  2. 如果点击到 E ,且其周围(八个方向 )无地雷,则改为 B ,并递归揭露所有相邻的未挖出方块(继续应用规则)。

  3. 如果点击到 E ,且周围有地雷,则改为对应数字(地雷个数),不继续递归

  4. 点击后返回更新后的盘面。

2.2 思路讲解

本题是典型的 FloodFill(洪水灌溉) 应用,但需注意八个方向(上下左右 + 四个对角线)。

三.代码演示

cpp 复制代码
class Solution {
public:
    int row,col;
    int bx[8] = {0,0,1,-1,-1,-1,1,1};
    int by[8] = {1,-1,0,0,-1,1,-1,1};
    vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) 
    {
        row = board.size(),col = board[0].size();
        int x = click[0],y = click[1];//点击坐标

        if(board[x][y] == 'M')//挖出地理
        {
            board[x][y] = 'X';
            return board;
        }
        //没挖出地理
        dfs(board,x,y);
        return board;
    }
    void dfs(vector<vector<char>>& board,int i,int j)
    {
        int count = 0;//地雷个数
        //先看看四周有没有地雷
        for(int k = 0;k < 8;k++)
        {
            int x = i + bx[k],y = j + by[k];
            if(x >= 0 && y >= 0 && x < row && y < col && board[x][y] == 'M')
            {
                count++;
            }
        }
        if(count)//存在地雷
        {
            board[i][j] = count + '0';
        }
        else
        {
            board[i][j] = 'B';//已经挖出
            for(int k = 0;k < 8;k++)
            {
                int x = i + bx[k],y = j + by[k];
                if(x >= 0 && y >= 0 && x < row && y < col && board[x][y] == 'E')
                {
                    dfs(board,x,y);
                }
            }
        }
        
    }
};

四.代码讲解

一、全局变量与方向数组
  • rowcol:成员变量,存储盘面的行数和列数。

  • 方向数组 bx[8]by[8]:表示八个方向(上下左右 + 四个对角线)的偏移量,用于探索相邻格子。 顺序为:右、左、下、上、左上、右上、左下、右下(顺序不影响结果)。

二、主函数 updateBoard
  1. 获取盘面尺寸row = board.size(); col = board[0].size();

  2. 获取点击坐标int x = click[0], y = click[1];

  3. 情况1:点到地雷 如果 board[x][y] == 'M',直接将其改为 'X',并立即返回盘面(游戏结束)。

  4. 情况2:点到未挖出的空方块 否则,调用 dfs(board, x, y) 进行递归揭露,最后返回更新后的盘面。

三、递归函数 dfs

dfs(board, i, j) 处理当前格子 (i, j)(此处保证 board[i][j] == 'E',即未挖出且非雷)。执行流程:

  1. 统计周围地雷个数 使用 for 循环遍历八个方向,计算新坐标 (x, y)。若坐标合法且该格子为 'M',则计数器 count 加 1。

  2. 根据周围地雷数量决定当前格子内容

    • 如果 count > 0 :将当前格子改为数字字符 count + '0'(表示相邻地雷个数),并停止递归(不继续探索相邻格子)。

    • 如果 count == 0 :将当前格子改为 'B'(空白块),然后递归地 对八个方向中所有为 'E' 的格子调用 dfs,继续揭露。

四、关键细节
  • 八个方向的覆盖:扫雷中相邻包括对角线,因此方向数组必须包含所有 8 个方向,否则会遗漏地雷计数或递归揭露范围。

  • 递归终止条件 :当周围有地雷时,只将当前格子改为数字,不继续递归;只有周围无雷时才深入相邻的 'E' 格子。这符合扫雷规则。

  • 避免重复访问 :递归过程中只对 'E' 格子进行,且一旦被处理(改为 'B' 或数字)就不再是 'E',因此不会重复进入。

  • 边界检查:每次访问相邻格子前,必须检查坐标是否在盘面范围内,防止越界。

五、流程图