130. 被围绕的区域(Surrounded Regions)

难度:中等 | 主题:图论、DFS、BFS、并查集

题目

给你一个 m x n 的矩阵 board,由若干字符 ‘X’ 和 ‘O’ 组成,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。被围绕的区间不会存在于边界上。

示例

 
输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
 
输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
 

思路

先讲个故事:隔离墙,边界上的逃犯

你是一个监狱长,要抓所有被包围的犯人(‘O’)。

但有个规则:边界上的犯人不算被包围——他们能逃到监狱外面。

怎么办?反向思维:不找被包围的,而是找能逃出去的(与边界连通的 ‘O’),给他们打上标记(‘T’)。最后,没被标记的 ‘O’ 就是被包围的,改成 ‘X’;标记的恢复成 ‘O’。


引导式推导:反向思维

核心洞察: 被 ‘X’ 包围的 ‘O’ 一定不连通到边界。

所以先从四条边上的 ‘O’ 出发 DFS/BFS,标记所有与边界连通的 ‘O’ 为临时字符。


flowchart TD

    A["从四条边界出发"] --> B["找到所有边界上的 O"]

    B --> C["DFS/BFS 标记连通的 O 为 T"]

    C --> D["遍历棋盘"]

    D --> E{"格子是?"}

    E -->|"T"| F["恢复为 O"]

    E -->|"O"| G["改为 X(被包围)"]

    E -->|"X"| H["保持不变"]


代码

 
class Solution:
 
    def solve(self, board: list[list[str]]) -> None:
 
        if not board or not board[0]:
 
            return
 
        m, n = len(board), len(board[0])
 
        def dfs(i: int, j: int):
 
            if i < 0 or i >= m or j < 0 or j >= n or board[i][j] != 'O':
 
                return
 
            board[i][j] = 'T'
 
            dfs(i + 1, j)
 
            dfs(i - 1, j)
 
            dfs(i, j + 1)
 
            dfs(i, j - 1)
 
        for i in range(m):
 
            dfs(i, 0)
 
            dfs(i, n - 1)
 
        for j in range(n):
 
            dfs(0, j)
 
            dfs(m - 1, j)
 
        for i in range(m):
 
            for j in range(n):
 
                if board[i][j] == 'T':
 
                    board[i][j] = 'O'
 
                elif board[i][j] == 'O':
 
                    board[i][j] = 'X'
 

复杂度

指标解释
时间O(m*n)每个格子访问一次
空间O(m*n)DFS 递归栈最坏情况

实战考量

频率分析

出现在:字节/美团 图论题,约 20% 常会考到。考察反向思维和边界处理

延伸思考

Q:为什么从边界出发而不是从中间出发?

A:边界上的 ‘O’ 一定不被包围,从它们扩散找连通块更高效。如果从中间出发,需要额外判断是否连通到边界。

Q:并查集能做吗?

A:可以,把所有 ‘O’ 和边界上的虚拟节点合并,最后不连通的改 ‘X’。

Q:空间复杂度能优化吗?

A:可以用 BFS + 队列,或用并查集;但最坏都是 O(m*n)。

Q:如果要求输出被包围区域的边界呢?

A:DFS 时记录访问过的 ‘O’ 的邻居中的 ‘X’,这些就是包围边界。

易错点

  • 四条边都要处理

  • 标记用临时字符 ‘T’,最后统一替换

  • 替换顺序:先 T→O,再 O→X


生活类比

隔离墙,找能逃出去的犯人

监狱长不找被包围的犯人,而是找能逃到边界外的

从边界开始,把所有能连通到外面的 ‘O’ 打上标记(‘T’)。

最后,没标记的 ‘O’ 就是被包围的,改成 ‘X’;标记的放回 ‘O’。

反向思维:找”不被包围的”,剩下的就是目标。


相关题目

题目关系
200岛屿数量连通块计数
417太平洋大西洋水流问题多边界扩散
695岛屿的最大面积连通块面积
130被围绕的区域本题

→ 返回题单:LeetCode学习路线图 > 十二、图论

速记卡(面试闪卡)

Q1:一句话讲清「130. 被围绕的区域(Surrounded Regions)」到底是什么?

A:把矩阵中被 X 完全包围的 O 全填成 X,反从边界连通的 O 标记保留。

Q2:题目理解 —— 什么 O 要被填? —— 怎么理解?

A:像监狱长抓犯人:边界上的 O 能逃到外面不算被围。你要填的是内部那些逃不掉的。英文:Surrounded Regions。

Q3:核心思路 —— 为什么从边界出发? —— 怎么理解?

A:像反向搜救:不找被围的,而从四条边出发标记所有能逃出去的 O 为 T,剩下的就是目标。英文:Reverse BFS/DFS。

Q4:代码实现 —— 三步怎么走? —— 怎么理解?

A:像贴标签再清场:先从四边 DFS 把连通 O 标 T;再遍历棋盘,T 复原 O、剩 O 填 X。英文:Mark-then-Fill。

Q5:复杂度与实战 —— 并查集也行? —— 怎么理解?

A:像另一种围法:把所有 O 和边界虚拟节点并查集合并,不连通的填 X;时间 O(mn)、空间最坏 O(mn)。英文:Union-Find。

Q6:核心速记主线有哪些?

  • 反向思维:从四条边界的 O 出发标记连通块为 T

  • 标记完遍历:T 复原 O,未被标记的 O 填 X

  • 四条边都要处理,临时字符 T 避免误改

  • 替换顺序:先 T→O,再 O→X

  • 并查集可做:O 与边界虚拟节点合并,不连通填 X

口诀

A:围绕区域反向寻,

边界逃出标记存;

连通留命余填 X,

清场一役净无尘。

相关链接