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 + 反向思考」模型

相关推荐
2401_841495642 小时前
【LeetCode刷题】二叉树的层序遍历
数据结构·python·算法·leetcode·二叉树··队列
AC赳赳老秦2 小时前
2026国产算力新周期:DeepSeek实战适配英伟达H200,引领大模型训练效率跃升
大数据·前端·人工智能·算法·tidb·memcache·deepseek
CodeSheep程序羊3 小时前
拼多多春节加班工资曝光,没几个敢给这个数的。
java·c语言·开发语言·c++·python·程序人生·职场和发展
独好紫罗兰3 小时前
对python的再认识-基于数据结构进行-a002-列表-列表推导式
开发语言·数据结构·python
2401_841495643 小时前
【LeetCode刷题】二叉树的直径
数据结构·python·算法·leetcode·二叉树··递归
budingxiaomoli3 小时前
优选算法-字符串
算法
编程小白20263 小时前
从 C++ 基础到效率翻倍:Qt 开发环境搭建与Windows 神级快捷键指南
开发语言·c++·windows·qt·学习
我是咸鱼不闲呀3 小时前
力扣Hot100系列19(Java)——[动态规划]总结(上)(爬楼梯,杨辉三角,打家劫舍,完全平方数,零钱兑换)
java·leetcode·动态规划
qq7422349843 小时前
APS系统与OR-Tools完全指南:智能排产与优化算法实战解析
人工智能·算法·工业·aps·排程
数智工坊3 小时前
【数据结构-树与二叉树】4.5 线索二叉树
数据结构