【每日一题】:934 最短的桥

51 阅读3分钟

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

今天的每日一题是934. 最短的桥 - 力扣(LeetCode)。看到关于岛屿的问题大概率都是关于广度优先搜索(BFS)深度优先搜索(DFS)的,这道题就是两种方法的结合。

题目

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

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

样例

输入: grid = [[0,1],[1,0]]
输出: 1

输入: grid = [[0,1],[1,0]]
输出: 1

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

方法

在题目中我们需要计算出一个最小的反转数目使得两个岛屿连接起来,这里就有一个限制条件就是整个数据中只有两个岛屿。那么我们可以这么想这个问题,我们只要两个岛的最外围的方块进行配对,计算出距离最近的路径长度就是需要反转的最小数据。

现在可以明确目标,反转的最小方块具体依赖如下两个条件:

  1. 匹配的方块是外围方块
  2. 分别数据不同岛屿的外围方块的最小距离

如何选择方块?

由于岛屿是连接在一起的方块,因此我们可以采用深度优先搜索(DFS)来确定出属于同一块岛屿的木块。在这这些木块中,如果它的四个方向存在为0的方块,说明他就是外围方块。

假设方块的坐标为 (x,y), 四个方向分别用 int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}表示,四个方向的坐标为(nx = x + dirs[d][0], ny = y + dirs[d][1])。当四个方向的坐标为0的时候就是外围方块。

其实选择方块的过程也可以采用广度优先搜索,因为我们的主要是目的就是筛选出一个岛屿所有方块,并进行判断它是不是外围方块。

如何计算出最小距离?

当得到最外围的方块时,我们可以逐渐将外围坐标向外扩散 1 格,直到有扩散的方块与另一个岛屿相遇,则当前扩散的次数就是最小的距离。这里扩散的方法可以采用广度优先搜索(BFS),因为广度优先搜索所它是优先将属于当前层外围方块都尽可能的向外扩散,而不是像深度优先搜索一样,优先去遍历当前一个外围方块。最小距离是一个全局的指标,应该一层层的将外围坐标向外扩散。

总结下来,整体代码如下:

// 定义四个方向
int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
    
public int shortestBridge(int[][] grid) {
    int n = grid.length;
    for (int i = 0; i < n; i++){
        for (int j = 0; j < n; j++){
            if (grid[i][j] == 1){
                Queue<int[]> queue = new ArrayDeque<>();
                // 利用dfs得到所有坐标
                dfs(i, j, grid, queue);
                int step = 0;
                while (!queue.isEmpty()){
                    int size = queue.size();
                    for (int k = 0; k < size; k++){
                        int[] cell = queue.poll();
                        int x = cell[0];
                        int y = cell[1];
                        for (int d = 0; d < dirs.length; d++){
                            int nx = x + dirs[d][0];
                            int ny = y + dirs[d][1];
                            if (nx >= 0 && ny >= 0 && nx < n && ny < n){
                                // 当前是外围方块,则利用BFS向外扩散,将新的方块插入到队列
                                if (grid[nx][ny] == 0){
                                    queue.offer(new int[]{nx, ny});
                                    grid[nx][ny] = -1;
                                }else if (grid[nx][ny] == 1){
                                    // 如果与另一个岛屿接触,则当前的step就是最小距离
                                    return step;
                                }
                            }
                        }
                    }
                    // 如果当前层的方块遍历完毕,距离+1,进入下一次扩散
                    step++;
                }
            }
        }
    }
    return 0;
}
public void dfs(int x, int y, int[][] grid, Queue<int[]> queue){
    if (x < 0 || y < 0 || x >= grid.length || y >= grid[0].length || grid[x][y] != 1){
        return;
    }
    queue.offer(new int[]{x, y});
    grid[x][y] = -1;

    for (int d = 0; d < dirs.length; d++){
        dfs(x + dirs[d][0], y + dirs[d][1], grid, queue);
    }
}