leetcode
leetcode 851 ~ 900
最短的桥

最短的桥

难度:

标签:

题目描述

代码结果

运行时间: 61 ms, 内存: 16.8 MB


// Java Stream Solution
// 使用Java Stream API实现的题解。我们依然首先找到第一个岛屿,然后使用BFS进行扩展,直到遇到第二个岛屿。

import java.util.*;
import java.util.stream.*;

public class Solution {
    private static final int[][] DIRECTIONS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

    public int shortestBridge(int[][] grid) {
        int n = grid.length;
        List<int[]> island1 = new ArrayList<>();
        boolean[][] visited = new boolean[n][n];

        // 找到第一个岛屿并标记
        outer:
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 1) {
                    dfs(grid, i, j, island1, visited);
                    break outer;
                }
            }
        }

        // BFS扩展岛屿
        int steps = 0;
        Queue<int[]> queue = new LinkedList<>(island1);
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int[] curr = queue.poll();
                for (int[] dir : DIRECTIONS) {
                    int x = curr[0] + dir[0];
                    int y = curr[1] + dir[1];
                    if (x >= 0 && x < n && y >= 0 && y < n && !visited[x][y]) {
                        if (grid[x][y] == 1) return steps;
                        if (grid[x][y] == 0) {
                            visited[x][y] = true;
                            queue.add(new int[]{x, y});
                        }
                    }
                }
            }
            steps++;
        }
        return -1; // 理论上不会到达这里
    }

    private void dfs(int[][] grid, int x, int y, List<int[]> island, boolean[][] visited) {
        int n = grid.length;
        if (x < 0 || x >= n || y < 0 || y >= n || grid[x][y] != 1 || visited[x][y]) return;
        visited[x][y] = true;
        island.add(new int[]{x, y});
        Arrays.stream(DIRECTIONS).forEach(dir -> dfs(grid, x + dir[0], y + dir[1], island, visited));
    }
}

解释

方法:

题解的核心思路是使用广度优先搜索(BFS)来找到连接两座岛屿的最短桥。首先,通过遍历矩阵来找到第一个岛屿,并使用一个队列标记所有属于这个岛屿的坐标,同时将这些位置在grid中标记为-1以表示已访问。接着,使用多源BFS从第一个岛屿的所有位置开始扩展,搜索最短的路径到第二个岛屿。每扩展一层,步数增加1,如果遇到未访问的陆地(值为1),则返回当前步数,这表示找到了第二个岛屿,且当前步数即为需要翻转的最小0的数量。

时间复杂度:

O(n*m)

空间复杂度:

O(n*m)

代码细节讲解

🦆
如何确保在遍历矩阵时,找到的第一个岛屿是最优的起始点,或者为什么选择第一个遇到的岛屿作为起点?
在解决最短桥问题时,选择第一个遇到的岛屿作为起点是基于实现的简便性而非最优性。算法的目标是找到两个岛屿之间的最短路径,而不依赖于起始岛屿的选择。因此,无论从哪个岛屿开始,只要能正确识别两个岛屿并执行多源BFS,最终都能找到最短的桥。
🦆
在使用BFS搜索最短路径时,如果同时遇到多个岛屿的边缘位置,算法是如何决定优先扩展哪个位置的?
BFS算法通过队列来保持扩展的顺序,遵循先进先出(FIFO)的原则。因此,首先遇到的边缘位置会首先被扩展。在多源BFS的情况下,所有当前步骤中可扩展的位置都将被同时加入队列,并在下一步骤中统一处理,这确保了搜索的层次性和公平性。
🦆
题解中多源BFS的`for step in range(n * m + 1)`循环中,为什么选择`n * m + 1`作为循环的上限,实际上是否可能需要这么多步骤?
选择`n * m + 1`作为循环上限是一种保守的设计,确保在极端情况下算法仍能运行完全。理论上,最短桥的长度不会超过网格的大小,但在实践中,通常不会需要这么多步骤。这个上限考虑了所有可能的位置都必须至少被访问一次的最糟情况。
🦆
在进行多源BFS时,每次扩展都可能会有大量的重复位置被加入队列中,这种情况下是否有更高效的方法来减少重复工作?
为了减少重复工作,可以在加入新位置到队列前,检查该位置是否已被访问或已经在队列中。通常,可以通过设置一个额外的状态数组或修改原地图的值来标记已经访问的位置。此外,使用集合代替队列来存储待扩展的位置也是一种方法,尽管这可能增加了空间复杂度,但可以有效避免重复扩展同一位置。

相关问题