LeetCode--130被围绕的区域

一、题目与目标

给定一个 m x n 的矩阵 board,元素为 'X''O'

  • 全部 'X' 包围的 'O' 区域要被翻转为 'X'

  • 与边界联通的 'O' 不能 被翻转

要求:原地修改 board


二、解题核心思路

反向思考

  • 真正不能被翻转的是:和边界上的 'O' 连通的所有 'O'

  • 其他 'O' 一定被 'X' 包围,可以翻成 'X'

做法:

  1. 从四条边上所有为 'O' 的格子出发,用 DFS(或 BFS)遍历,把所有和边界连通的 'O' 暂时标记为 '#'

  2. 全图扫描:

    • 遇到 'O':说明它不与边界连通 → 翻成 'X'

    • 遇到 '#':说明它和边界连通 → 还原成 'O'

这样就能区分"能翻转的 O"和"不能翻转的 O"。


三、算法步骤

假设 m 为行数,n 为列数。

  1. 处理左右边界 (第 0 列和第 n-1 列)

    对每一行 i

    • 如果 board[i][0] == 'O',从 (i, 0) 开始 DFS,标记连通块为 '#'

    • 如果 board[i][n - 1] == 'O',从 (i, n-1) 开始 DFS

  2. 处理上下边界 (第 0 行和第 m-1 行)

    对每一列 j

    • 如果 board[0][j] == 'O',从 (0, j) 开始 DFS

    • 如果 board[m - 1][j] == 'O',从 (m-1, j) 开始 DFS

  3. 全表扫描进行翻转

    双重循环遍历整个矩阵:

    • board[i][j] == 'O':翻成 'X'

    • board[i][j] == '#':还原成 'O'


四、代码实现与解析

1. 完整代码

cpp 复制代码
class Solution {
public:
    void solve(vector<vector<char>>& board) {
        // m 表示行数,n 表示列数
        int m = board.size(), n = board[0].size();

        // 1. 处理左右边界
        for (int i = 0; i < m; ++i) {
            if (board[i][0] == 'O') {
                dfs(i, 0, board, n, m);
            }
            if (board[i][n - 1] == 'O') {
                dfs(i, n - 1, board, n, m);
            }
        }

        // 2. 处理上下边界
        for (int j = 0; j < n; ++j) {
            if (board[0][j] == 'O') {
                dfs(0, j, board, n, m);
            }
            if (board[m - 1][j] == 'O') {
                dfs(m - 1, j, board, n, m);
            }
        }

        // 3. 全表扫描,翻转 / 还原
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (board[i][j] == 'O') {
                    board[i][j] = 'X';     // 被包围,翻转
                } else if (board[i][j] == '#') {
                    board[i][j] = 'O';     // 与边界连通,还原
                }
            }
        }
    }

private:
    // upDown: 行下标,lr: 列下标
    void dfs(int upDown, int lr,
             vector<vector<char>>& board,
             int& n, int& m) {

        // 越界直接返回
        if (upDown < 0 || lr < 0 || lr > n - 1 || upDown > m - 1) return;

        // 只处理 'O'
        if (board[upDown][lr] != 'O') return;

        // 标记为 '#'
        board[upDown][lr] = '#';

        // 四个方向扩展
        dfs(upDown,     lr + 1, board, n, m); // 右
        dfs(upDown,     lr - 1, board, n, m); // 左
        dfs(upDown + 1, lr,     board, n, m); // 下
        dfs(upDown - 1, lr,     board, n, m); // 上
    }
};

说明:mndfs 中按引用传递只是为了少拷贝,按值传也可以,不影响逻辑。


2. 关键点解析

  1. 从边界开始 DFS 而不是从内部

    所有从边界 'O' 出发能走到的 'O' 都是"安全区",临时标记为 '#' 保留。

  2. DFS 的访问条件

    • 先判断越界:upDownlr[0, m-1][0, n-1] 范围内

    • 再判断是否为 'O',不是 'O' 直接返回

  3. 标记与最终翻转的分离

    • DFS 只负责标记 '#'

    • 最后统一遍历一遍矩阵进行 翻转 + 还原

这样保证逻辑清晰、实现简单。


五、时间与空间复杂度

  • 每个格子最多被访问和标记一次

    时间复杂度O(m * n)

  • 递归调用栈最坏深度为 O(m * n)(极端全为 'O' 的情况)

    空间复杂度 :最坏 O(m * n)(递归栈)


六、小结要点

  1. 四条边 上的 'O' 出发,找出所有和边界联通的 'O'

  2. 用临时字符 '#' 标记"边界联通区"

  3. 最后统一遍历:

    • 'O''X'

    • '#''O'

整套解法就是「边界 DFS + 反向思考」模型

相关推荐
NAGNIP1 天前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
端平入洛1 天前
delete又未完全delete
c++
颜酱1 天前
单调栈:从模板到实战
javascript·后端·算法
CoovallyAIHub2 天前
仿生学突破:SILD模型如何让无人机在电力线迷宫中发现“隐形威胁”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
从春晚机器人到零样本革命:YOLO26-Pose姿态估计实战指南
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
Le-DETR:省80%预训练数据,这个实时检测Transformer刷新SOTA|Georgia Tech & 北交大
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
强化学习凭什么比监督学习更聪明?RL的“聪明”并非来自算法,而是因为它学会了“挑食”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
YOLO-IOD深度解析:打破实时增量目标检测的三重知识冲突
深度学习·算法·计算机视觉
NAGNIP2 天前
轻松搞懂全连接神经网络结构!
人工智能·算法·面试
NAGNIP2 天前
一文搞懂激活函数!
算法·面试