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

简介: 给你一个大小为 n x n 的二元矩阵 grid ,其中 1 表示陆地,0 表示水域。岛 是由四面相连的 1 形成的一个最大组,即不会与非组内的任何其他 1 相连。grid 中 恰好存在两座岛 。

最短的桥【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)

目录
相关文章
|
2月前
【每日一题Day168】LC2427公因子的数目 | 模拟
【每日一题Day168】LC2427公因子的数目 | 模拟
24 1
|
2月前
|
存储 人工智能 算法
【每日一题Day348】LC137只出现一次的数字Ⅱ | 状态转移
【每日一题Day348】LC137只出现一次的数字Ⅱ | 状态转移
29 0
|
2月前
【每日一题Day356】LC2678老人的数目 | 字符串
【每日一题Day356】LC2678老人的数目 | 字符串
27 0
|
2月前
|
C#
【每日一题Day251】LC2490回环句 | 模拟
【每日一题Day251】LC2490回环句 | 模拟
24 0
|
2月前
【每日一题Day342】LC2578最小和分割 | 贪心
【每日一题Day342】LC2578最小和分割 | 贪心
25 0
|
2月前
【每日一题Day243】LC1595连通两组点的最小成本 | 状态压缩dp
【每日一题Day243】LC1595连通两组点的最小成本 | 状态压缩dp
36 0
|
2月前
【每日一题Day160】LC1092最短公共超序列 | 动态规划
【每日一题Day160】LC1092最短公共超序列 | 动态规划
25 0
【每日一题Day160】LC1092最短公共超序列 | 动态规划
|
2月前
leetcode-934:最短的桥
leetcode-934:最短的桥
26 0
|
2月前
|
图计算
【每日一题Day274】LC42接雨水 | 单调栈
【每日一题Day274】LC42接雨水 | 单调栈
30 0
|
2月前
【每日一题Day330】LC337打家劫舍Ⅲ | 动态规划
【每日一题Day330】LC337打家劫舍Ⅲ | 动态规划
24 0