被围绕的区域

这题别正着想“哪些 O 会被吃掉”,而要反着想“哪些 O 一定不能被吃掉”。

问题描述

LeetCode 130 - 被围绕的区域

给定一个二维矩阵,包含’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’
  • DFS/BFS:从边界’O’进行DFS/BFS
  • 标记保留:边界连通的’O’标记为临时字符
  • 填充转换:未标记的’O’变为’X’,临时字符恢复为’O’

为什么要从边界反推

真正不会被包围的 'O',一定满足:

  • 它在边界上,或者
  • 它能连到边界上的 'O'

所以与其去猜哪些 'O' 会被翻掉,不如先把所有“安全区”从边界出发整块标出来。
最后剩下那些没被保护到的 'O',才是应该翻成 'X' 的部分。

Go 代码

Go 实现

func solve(board [][]byte) {
    if len(board) == 0 {
        return
    }
 
    rows, cols := len(board), len(board[0])
 
    var dfs func(int, int)
    dfs = func(r, c int) {
        if r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] != 'O' {
            return
        }
 
        board[r][c] = '#'
 
        dfs(r+1, c)
        dfs(r-1, c)
        dfs(r, c+1)
        dfs(r, c-1)
    }
 
    // 从边界DFS
    for r := 0; r < rows; r++ {
        if board[r][0] == 'O' {
            dfs(r, 0)
        }
        if board[r][cols-1] == 'O' {
            dfs(r, cols-1)
        }
    }
 
    for c := 0; c < cols; c++ {
        if board[0][c] == 'O' {
            dfs(0, c)
        }
        if board[rows-1][c] == 'O' {
            dfs(rows-1, c)
        }
    }
 
    // 转换
    for r := 0; r < rows; r++ {
        for c := 0; c < cols; c++ {
            if board[r][c] == 'O' {
                board[r][c] = 'X'
            } else if board[r][c] == '#' {
                board[r][c] = 'O'
            }
        }
    }
}

易错点

这题最容易错的地方,是边界扫描不完整或者标记字符处理不干净。

  • 四条边都要扫到,不能只扫两条。
  • 临时标记字符要和原字符集区分开,比如用 #。
  • 最后别忘了做两次转换:O -> X,# -> O。
  • 这题和岛屿数量一样,本质上也是网格连通块问题。

复杂度分析

指标复杂度说明
时间复杂度O(M×N)遍历所有格子
空间复杂度O(M×N)递归栈(最坏情况)

相关主题


返回:图算法 | 算法学习导航