[路飞]被围绕的区域

56 阅读2分钟

记录 1 道算法题

被围绕的区域

130. 被围绕的区域 - 力扣(LeetCode)


要求:提供一个 m * n 的矩阵,里面填充字符 X 或 O,将被 X 围绕的区域中的 O 填充为 X。在边上的 O 跟与之相连的 O 不视为被围绕。

比如:

[    ["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"]
]
  1. dfs / bfs

可以沿着 4 个边通过 bfs 或者 dfs 的方式不重复的进行探索,将探索到的 O 标记为 A,这时候不被围绕的就是 A,剩下的 O 则是被围绕,然后遍历全部,将 A 变成 O,将 O 变成 X 即可。

    function solve(board) {
        const row = board.length
        const col = board[0].length
        const queue = []
        
        // 从左右两个边开始收集
        for (let i = 0; i < row; i++) {
            if (board[i][0] === 'O') {
                queue.push([i, 0])
                board[i][0] = 'A'
            }
            if (board[i][col - 1] === 'O') {
                queue.push([i, col - 1])
                board[i][col - 1] = 'A'
            }
        }
        
        // 从上下两个边开始收集 四个角不重复
        for (let i = 1; i < col - 1; i++) {
            if (board[0][i] === 'O') {
                queue.push([0, i])
                board[0][i] = 'A'
            }
            if (board[row - 1][i] === 'O') {
                queue.push([row - 1, i])
                board[row - 1][i] = 'A'
            }
        }
        
        const transfer = (x, y) => {
            // 在允许的范围内寻找 O
            if (x < 0 || x >= col || y < 0 || y >= row || board[x][y] !== 'O') {
                return
            }
            
            board[x][y] = 'A'
            queue.push([x,y])
        }
        
        // 深度遍历构建关系
        while(queue.length) {
            const [x, y] = queue.pop()
            transfer(x - 1, y)
            transfer(x + 1, y)
            transfer(x, y - 1)
            transfer(x, y + 1)
        }
        
        // 将 A 转化为 O,将 O 转换为 X
        for(let i = 0; i < row; i++) {
            for(let j = 0;  j < col; j++) {
                if (board[i][j] === 'A') {
                    board[i][j] = 'O'
                } else if (board[i][j] === 'O') {
                    board[i][j] = 'X'
                }
            }
        }
        
        return board
    }
  1. 并查集

收集关系可以使用并查集,如果是与 4 条边上的字符处于同一个集合,那么意味着不是被围绕的区域。

下面的代码只是一个思路,有不少可以优化的地方。

    function solve(board) {
        const row = board.length
        const col = board[0].length
        const union = Array.from(Array(row * col), (_, i) => i)
        // 二维坐标转一维坐标
        function computeIndex(x, y, size) {
            return x * size + y
        }
        
        // 将 4 条边都收集到某一个点的集合下,这里收集到左上角 0。
        for (let i = 0; i < row; i++) {
          if (board[i][0] === 'O') {
            union[computeIndex(i, 0, row)] = 0
          }
          if (board[i][col - 1] === 'O') {
            union[computeIndex(i, col - 1, row)] = 0
          }
        }

        for (let i = 1; i < col - 1; i++) {
          if (board[0][i] === 'O') {
            union[computeIndex(0, i, row)] = 0
          }
          if (board[row - 1][i] === 'O') {
            union[computeIndex(row - 1, i, row)] = 0
          }
        }
        // 并查集的查找和压缩
        const find = (i) {
            while(union[i] !== i) {
                union[i] = union[union[i]]
                i = union[i]
            }
            
            return i
        }
        // 确认并建立两个集合的联系
        const connect = (a, b) => {
            const n1 = find(a)
            const n2 = find(b)
            if (n1 !== n2) {
                // 将大的数归到小的数的集合里
                n1 < n2 ? (union[n2] = n1) : (union[n1] = n2)
            }
        }
        // 添加集合
        for (let i = 1; i < row - 1; i++) {
            for (let j = 1; j < col - 1; j++) {
                if (board[i][j] === 'O') {
                    // 4 个方向上查找,存在重复的情况,需要优化
                    const index = computeIndex(i, j, row)
                    board[i + 1][j] === 'O' &&
                        connect(index, computeIndex(i + 1, j, row))
                    board[i - 1][j] === 'O' &&
                        connect(index, computeIndex(i - 1, j, row))
                    board[i][j + 1] === 'O' &&
                        connect(index, computeIndex(i, j + 1, row))
                    board[i][j - 1] === 'O' &&
                        connect(index, computeIndex(i, j - 1, row))
                }
            }
        }
        
        // 将跟左上角 0 同一个集合的字符都转成 X
        for (let i = 1; i < row - 1; i++) {
            for (let j = 1; j < col - 1; j++) {
                const index = computeIndex(i, j, row)
                if (index === find(0) && board[i][j] === 'O') {
                    board[i][j] = 'X'
                }
            }
        }
        
        return board
    }