LeetCode 130-被围绕的区域

88 阅读4分钟

题目

给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' 组成,捕获 所有 被围绕的区域

  • 连接: 一个单元格与水平或垂直方向上相邻的单元格连接。
  • 区域:连接所有 'O' 的单元格来形成一个区域。
  • 围绕: 如果您可以用 'X' 单元格 连接这个区域,并且区域中没有任何单元格位于 board 边缘,则该区域被 'X' 单元格围绕。

通过将输入矩阵 board 中的所有 'O' 替换为 'X' 来 捕获被围绕的区域

示例 1:

输入: 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"]]

解释:

在上图中,底部的区域没有被捕获,因为它在 board 的边缘并且不能被围绕。

示例 2:

输入: board = [["X"]]

输出: [["X"]]

 

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 200
  • board[i][j] 为 'X' 或 'O'

思路

本题三种元素:

  • 字母"X"
  • 被字母"X"包围的"O"
  • 不被字母"X"包围的"O"

要求将所有被X包围的O变成X,如何判断O被包围或者不被包围。

题目解释中提到:任何边界上的 O 都不会被填充为 X。 我们可以想到,所有的不被包围的 O 都直接或间接与边界上的 O 相连。我们可以利用这个性质判断 O 是否在边界上。

具体地说:

  • 对于每一个边界上的 O,我们以它为起点,标记所有与它直接或间接相连的字母 O;
  • 最后我们遍历这个矩阵,对于每一个字母:
    • 如果该字母被标记过,则该字母为没有被字母 X 包围的字母 O,我们将其还原为字母 O;
    • 如果该字母没有被标记过,则该字母为被字母 X 包围的字母 O,我们将其修改为字母 X。

解法一: DFS

解法一: DFS

class Solution:
    def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        def dfs(r, c):
            if r < 0 or r >= nr or c < 0 or c >= nc or board[r][c] != 'O':
                return

            # 标记
            board[r][c] = 'A'
            dfs(r+1, c)
            dfs(r-1, c)
            dfs(r, c+1)
            dfs(r, c-1)
    
        nr, nc = len(board), len(board[0])
        for r in range(nr):
            dfs(r, 0)
            dfs(r, nc-1)
        
        for c in range(nc):
            dfs(0, c)
            dfs(nr-1, c)
        
        for r in range(nr):
            for c in range(nc):
                if board[r][c] == "A":
                    board[r][c] = 'O'
                elif board[r][c] == 'O':
                    board[r][c] = 'X'

解法二: BFS

解法二: BFS

import collections


class Solution:
    def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        nr, nc = len(board), len(board[0])
        q = collections.deque()
        for r in range(nr):
            if board[r][0] == "O":
                q.append((r, 0))
                board[r][0] = "A"
            if board[r][nc-1] == "O":
                q.append((r, nc-1))
                board[r][nc-1] = "A"
        for c in range(nc):
            if board[0][c] == "O":
                q.append((0, c))
                board[0][c] = "A"
            if board[nr-1][c] == "O":
                q.append((nr-1, c))
                board[nr-1][c] = "A"
        
        while q:
            r, c = q.popleft()
            for next_r, next_c in [(r+1, c), (r-1, c), (r, c+1), (r, c-1)]:
                if next_r >= 0 and next_r < nr and next_c >= 0 and next_c < nc and board[next_r][next_c] == 'O':
                    q.append((next_r, next_c))
                    board[next_r][next_c] = 'A'
        
        for r in range(nr):
            for c in range(nc):
                if board[r][c] == "A":
                    board[r][c] = 'O'
                elif board[r][c] == 'O':
                    board[r][c] = "X"

解法三: 并查集

将四周的O和哨兵连接起来,然后遍历数组将所有与边界联通的连接。最后遍历board,判断当前的'0'是否和哨兵连接,如果无连接,说明被X完全包围,将O变为X。

解法三: 并查集

class Solution:
    def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        nr, nc = len(board), len(board[0])
        dummyNode = nr * nc

        uf = UnionFind(nr*nc+1)
        for r in range(nr):
            for c in range(nc):
                if board[r][c] == 'O':
                    if r == 0 or r == nr-1 or c == 0 or c == nc-1:
                        uf.union(r*nc+c, dummyNode)
                    
                    else:
                        if r-1 >= 0 and board[r-1][c] == "O":
                            uf.union(r*nc+c, (r-1)*nc+c)
                        if r+1 < nr and board[r+1][c] == "O":
                            uf.union(r*nc+c, (r+1)*nc+c)
                        if c-1 >= 0 and board[r][c-1] == "O":
                            uf.union(r*nc+c, r*nc+c-1)
                        if c+1 < nc and board[r][c+1] == "O":
                            uf.union(r*nc+c, r*nc+c+1)
    
        for r in range(nr):
            for c in range(nc):
                if not uf.isConnected(r*nc+c, dummyNode):
                    board[r][c] = 'X'




class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
    
    def find(self, x):
        if self.parent[x] != x:
           # 如果只是简单的查找,会超时,在查找时最好赋值,下次查找更便捷 
           self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, x, y):
        rootx = self.find(x)
        rooty = self.find(y)
        if rootx != rooty:
            self.parent[rootx] = rooty
    
    def isConnected(self, x, y):
        return self.find(x) == self.find(y)