【每日一题Day7】LC934.最短的桥

最短的桥【LC934】

给你一个大小为 n x n 的二元矩阵 grid ,其中 1 表示陆地,0 表示水域。

是由四面相连的 1 形成的一个最大组,即不会与非组内的任何其他 1 相连。grid恰好存在两座岛

你可以将任意数量的 0 变为 1 ,以使两座岛连接起来,变成 一座岛

返回必须翻转的 0 的最小数目。

今天是cv选手

BFS
  • 思路:首先先找到其中一座岛;然后将其不断向外延伸一圈,直到到达了另一座岛,延伸的圈数即为做短距离

  • 实现:

    • 使用BFS,将已经其中一座岛的位置标记为-1,并放入队列中;
    • 然后从队列中的所有位置开始BFS
      • 达到任意的1,就返回搜索的层数
      • 达到任意的0(水域),就将其设置为-1,即为第一座岛的延伸
  • 代码

    class Solution {
        public int shortestBridge(int[][] grid) {
            int n = grid.length;
            int[][] dirs = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
            List<int[]> island = new ArrayList<int[]>();
            Queue<int[]> queue = new ArrayDeque<int[]>();
    
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (grid[i][j] == 1) {
                        queue.offer(new int[]{i, j});
                        grid[i][j] = -1;
                        // 找到第一座岛的所有点
                        while (!queue.isEmpty()) {
                            int[] cell = queue.poll();
                            int x = cell[0], y = cell[1];
                            island.add(cell);// 将所有点放入island中
                            for (int k = 0; k < 4; k++) {
                                int nx = x + dirs[k][0];
                                int ny = y + dirs[k][1];
                                // 若该点周围的点是陆地,那么放入queue中
                                if (nx >= 0 && ny >= 0 && nx < n && ny < n && grid[nx][ny] == 1) {
                                    queue.offer(new int[]{nx, ny});
                                    grid[nx][ny] = -1;
                                }
                            }
                        }
                        for (int[] cell : island) {
                            queue.offer(cell);// 第一座岛的所有点
                        }
                        int step = 0;
                        // BFS 将这座岛的点不断向外延伸
                        while (!queue.isEmpty()) {
                            int sz = queue.size();
                            for (int k = 0; k < sz; k++) {
                                int[] cell = queue.poll();
                                int x = cell[0], y = cell[1];
                                for (int d = 0; d < 4; d++) {
                                    int nx = x + dirs[d][0];
                                    int ny = y + dirs[d][1];
                                    if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
                                        if (grid[nx][ny] == 0) {// 如果是水域,将该节点“同化”为第一座岛屿,向前走一步
                                            queue.offer(new int[]{nx, ny});// 加入新轮次节点
                                            grid[nx][ny] = -1;
                                        } else if (grid[nx][ny] == 1) {// 如果是陆地 那么返回当前步数
                                            return step;
                                        }
                                    }
                                }
                            }
                            step++;// 遍历完所有该轮次节点 向前走一步
                        }
                    }
                }
            }
            return 0;
        }
    }
    
    作者:力扣官方题解
    链接:https://leetcode.cn/problems/shortest-bridge/solutions/1920297/zui-duan-de-qiao-by-leetcode-solution-qe44/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    
  • 复杂度

    • 时间复杂度:O(n2)
    • 空间复杂度:O(n2)
并查集+双向BFS
  • 思路:使用并查集将两个岛标记出来,然后将两个岛的点分别入队,在运用双向BFS来找最短通路。

  • 实现

    1. 对于所有满足 grid[i][j]=1 的节点与其四联通的方向,值同为 1 的节点进行并查集连通性维护
    2. 随后建立两个队列 d1 和 d2 分别存储两个岛的节点(以二元组 (x,y) 的方式出入队),并使用两个哈希表 m1 和 m2 来记录从两岛屿出发到达该节点所消耗的步数(以节点的一维编号为 key,以消耗步数为 value)
    3. 最后是使用**「双向 BFS」**来求解使两岛屿联通的最小通路:每次从队列中较少的进行拓展,只有尚未被处理过的节点(没有被当前哈希表所记录)才进行入队并更新消耗步数,当拓展节点在另外一个队列对应的哈希表表中出现过,说明找到了最短通路。
  • 代码

    class Solution {
        static int N = 10010;
        static int[] p = new int[N];
        static int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
        int n;
        int getIdx(int x, int y) {
            return x * n + y;
        }
        int find(int x) {
            if (p[x] != x) p[x] = find(p[x]);
            return p[x];
        }
        void union(int x, int y) {
            p[find(x)] = p[find(y)];
        }
        public int shortestBridge(int[][] g) {
            n = g.length;
            for (int i = 0; i <= n * n; i++) p[i] = i;// 初始化父节点
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (g[i][j] == 0) continue;
                    for (int[] di : dirs) {
                        int x = i + di[0], y = j + di[1];
                        if (x < 0 || x >= n || y < 0 || y >= n) continue;
                        if (g[x][y] == 0) continue;
                        union(getIdx(i, j), getIdx(x, y));// 区分两个岛屿
                    }
                }
            }
            int a = -1, b = -1;
            Deque<int[]> d1 = new ArrayDeque<>(), d2 = new ArrayDeque<>();
            Map<Integer, Integer> m1 = new HashMap<>(), m2 = new HashMap<>();
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (g[i][j] == 0) continue;
                    int idx = getIdx(i, j), root = find(idx);
                    if (a == -1) a = root;    
                    else if (a != root && b == -1) b = root;
                    if (root == a) {
                        d1.addLast(new int[]{i, j});// 存放岛屿1
                        m1.put(idx, 0);// 存放下标和对应的步数
                    } else if (root == b) {
                        d2.addLast(new int[]{i, j});// 存放岛屿2
                        m2.put(idx, 0);
                    }
                }
            }
            while (!d1.isEmpty() && !d2.isEmpty()) {
                int t = -1;
                if (d1.size() < d2.size()) t = update(d1, m1, m2);
                else t = update(d2, m2, m1);
                if (t != -1) return t - 1;
            }
            return -1; // never
        }
        int update(Deque<int[]> d, Map<Integer, Integer> m1, Map<Integer, Integer> m2) {
            int sz = d.size();
            while (sz-- > 0) {
                int[] info = d.pollFirst();
                int x = info[0], y = info[1], idx = getIdx(x, y), step = m1.get(idx);
                for (int[] di : dirs) {
                    int nx = x + di[0], ny = y + di[1], nidx = getIdx(nx, ny);
                    if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue;
                    if (m1.containsKey(nidx)) continue;
                    //当拓展节点在另外一个队列对应的哈希表表中出现过,说明找到了最短通路,返回结果
                    if (m2.containsKey(nidx)) return step + 1 + m2.get(nidx);
                    d.addLast(new int[]{nx, ny});
                    m1.put(nidx, step + 1);
                }
            }
            return -1;
        }
    }
    作者:宫水三叶
    链接:https://leetcode.cn/problems/shortest-bridge/solutions/1922355/by-ac_oier-56ly/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    
  • 复杂度

    • 时间复杂度:O(n2)
    • 空间复杂度:O(n2)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值