[JavaScript / leetcode] 934. 最短的桥

81 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情

每日刷题 2022.10.25

题目

  • 给你一个大小为 n x n 的二元矩阵 grid ,其中 1 表示陆地,0 表示水域。
  • 岛 是由四面相连的 1 形成的一个最大组,即不会与非组内的任何其他 1 相连。grid 中 恰好存在两座岛 。
  • 你可以将任意数量的 0 变为 1 ,以使两座岛连接起来,变成 一座岛 。
  • 返回必须翻转的 0 的最小数目。

示例

  • 示例1
输入: grid = [[0,1],[1,0]]
输出: 1
  • 示例2
输入: grid = [[0,1,0],[0,0,0],[0,0,1]]
输出: 2
  • 示例3
输入: grid = [[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]]
输出: 1

提示

  • n == grid.length == grid[i].length
  • 2 <= n <= 100
  • grid[i][j] 为 0 或 1
  • grid 中恰有两个岛

解题思路

  • 经典的 bfs 所有的节点的权值都为 1,但如果我们把某些点的权值视为 0 ,即经过这个点不需要花销,则转变为了 0-1 bfs
  • 经典的 bfs 使用一个队列,队列中层级小的点在队列前面,新的节点在队尾入队,这样可以优先遍历层级小的节点
  • 0-1 bfs 使用双端队列,新节点入队时,若权值为 0 则入对头,否则入队尾,这样可以优先处理权值为 0 的节点
  • 此题中值为 1 的点权值为 0 ,值为 0 的点权值为 1
  • 先找到一个值为1的点视为起点,然后利用 0-1 bfs 计算所有点到他的最短距离(即相连需要反转的 0 的最小个数)
  • 时间复杂度:O(n^2) 空间复杂度:O(n^2)

其他的解法

  • 并查集+ 双端bfs

AC代码

/**
 * @param {number[][]} grid
 * @return {number}
 */
var shortestBridge = function (grid) {
    const d = [-1, 0, 1, 0, -1];
    const n = grid.length;
    const que = [];
    const dfs = (x, y) => {
        if (x < 0 || y < 0 || x >= n || y >= n || grid[x][y] !== 1) return;
        que.push([x, y]);
        grid[x][y] = 11; //标记为岛1的点,剩下为1的都是岛2 的点
        for (let di = 0; di < 4; di++) dfs(x + d[di], y + d[di + 1]);
    };
    for (let i = 0; i < n; i++) {
        let breakFlag = false;
        for (let j = 0; j < n; j++) {
            if (grid[i][j] === 1) {
                dfs(i, j);
                breakFlag = true;
                break;
            }
        }
        if (breakFlag) break;
    }
    let step = 0;
    //BFS
    while (que.len !== 0) {
        const sz = que.length;
        for (let k = 0; k < sz; k++) {
            const cur = que.shift(); //从队列头部取才保证是在岛1的外层
            for (let l = 0; l < 4; l++) {
                const dx = cur[0] + d[l];
                const dy = cur[1] + d[l + 1];
                if (dx >= 0 && dy >= 0 && dx < n && dy < n) {
                    if (grid[dx][dy] === 0) {
                        //是海就是边缘,加入队列用于下一次
                        que.push([dx, dy]);
                        grid[dx][dy] = 2; //标记为访问过
                    } else if (grid[dx][dy] === 1)
                        //遇到 岛2 的点了
                        return step;
                }
            }
        }
        step++;
    }
    return 0; //其实题目确保有路的话是不会走到这里的
};