diff --git "a/Index/\345\233\276\350\256\272 BFS.md" "b/Index/\345\233\276\350\256\272 BFS.md" index 09731922..f013fc7f 100644 --- "a/Index/\345\233\276\350\256\272 BFS.md" +++ "b/Index/\345\233\276\350\256\272 BFS.md" @@ -6,4 +6,5 @@ | [773. 滑动谜题](https://leetcode-cn.com/problems/sliding-puzzle/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/sliding-puzzle/solution/gong-shui-san-xie-fa-hui-a-suan-fa-zui-d-3go8/) | 困难 | 🤩🤩🤩🤩 | | [815. 公交路线](https://leetcode-cn.com/problems/bus-routes/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/bus-routes/solution/gong-shui-san-xie-yi-ti-shuang-jie-po-su-1roh/) | 困难 | 🤩🤩🤩🤩 | | [909. 蛇梯棋](https://leetcode-cn.com/problems/snakes-and-ladders/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/snakes-and-ladders/solution/gong-shui-san-xie-bfs-mo-ni-by-ac_oier-woh6/) | 中等 | 🤩🤩🤩🤩 | +| [1162. 地图分析](https://leetcode-cn.com/problems/as-far-from-land-as-possible/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/as-far-from-land-as-possible/solution/gong-shui-san-xie-ru-he-shi-yong-duo-yua-vlea/) | 中等 | 🤩🤩🤩🤩 | diff --git "a/LeetCode/1051-1160/1162. \345\234\260\345\233\276\345\210\206\346\236\220\357\274\210\344\270\255\347\255\211\357\274\211.md" "b/LeetCode/1051-1160/1162. \345\234\260\345\233\276\345\210\206\346\236\220\357\274\210\344\270\255\347\255\211\357\274\211.md" new file mode 100644 index 00000000..0aa64665 --- /dev/null +++ "b/LeetCode/1051-1160/1162. \345\234\260\345\233\276\345\210\206\346\236\220\357\274\210\344\270\255\347\255\211\357\274\211.md" @@ -0,0 +1,194 @@ +### 题目描述 + +这是 LeetCode 上的 **[1162. 地图分析](https://leetcode-cn.com/problems/as-far-from-land-as-possible/solution/gong-shui-san-xie-ru-he-shi-yong-duo-yua-vlea/)** ,难度为 **中等**。 + +Tag : 「图论 BFS」、「多源 BFS」 + + + +你现在手里有一份大小为 $N * N$ 的 网格 $grid$,上面的每个 单元格 都用 $0$ 和 $1$ 标记好了。 + +其中 $0$ 代表海洋,$1$ 代表陆地,请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的。 + +我们这里说的距离是「曼哈顿距离」:$(x0, y0)$ 和 $(x1, y1)$ 这两个单元格之间的距离是 $|x0 - x1| + |y0 - y1|$。 + +如果网格上只有陆地或者海洋,请返回 $-1$。 + +示例 1: +![](https://files.mdnice.com/user/9208/4ad5ca7f-bd14-47fc-adc4-760c29ba1bee.png) +``` +输入:[[1,0,1],[0,0,0],[1,0,1]] + +输出:2 + +解释:海洋单元格 (1, 1) 和所有陆地单元格之间的距离都达到最大,最大距离为 2。 +``` +示例 2: +![](https://files.mdnice.com/user/9208/cec486ae-45a4-4235-87b4-89f995f07d3e.png) +``` +输入:[[1,0,0],[0,0,0],[0,0,0]] + +输出:4 + +解释:海洋单元格 (2, 2) 和所有陆地单元格之间的距离都达到最大,最大距离为 4。 +``` + +提示: +* 1 <= grid.length == grid[0].length <= 100 +* grid[i][j] 不是 $0$ 就是 $1$ + +--- + +### 单源 BFS + +通常我们使用 BFS 求最短路,都是针对如下场景:从特定的起点出发,求解到达特定终点的最短距离。 + +**这是一类特殊的「单源最短路」问题:本质是在一个边权为 $1$ 的图上,求从特定「源点」出发到达特定「汇点」的最短路径。** + +对于本题,如果套用「单源最短路」做法,我们需要对每个「海洋」位置做一次 BFS:求得每个「海洋」的最近陆地距离,然后在所有的距离中取 $max$ 作为答案。 + +单次 BFS 的最坏情况需要扫描完整个矩阵,复杂度为 $O(n^2)$。 + +同时,最多有 $n^2$ 个海洋区域需要做 BFS,因此这样的做法复杂度为 $O(n^4)$,并且 $O(n^4)$ 可直接取满。 + +PS. 数据范围为 $10^2$,理论上是一定会超时,但本题数据较弱,Java 2021/06/28 可过。 + +一些细节:为了方便,我们在使用哈希表记录距离时,将二维坐标 $(x, y)$ 转化为对应的一维下标 $idx = x * n + y$ 作为 key 进行存储。 + +代码: +```Java [] +class Solution { + int n; + int[][] grid; + public int maxDistance(int[][] _grid) { + grid = _grid; + n = grid.length; + int ans = -1; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 0) { + ans = Math.max(ans, bfs(i, j)); + } + } + } + return ans; + } + // 单次 BFS:求解海洋位置 (x,y) 最近的陆地距离 + int bfs(int x, int y) { + int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}}; + Deque d = new ArrayDeque<>(); + Map map = new HashMap<>(); + d.addLast(new int[]{x, y}); + map.put(x * n + y, 0); + while (!d.isEmpty()) { + int[] poll = d.pollFirst(); + int dx = poll[0], dy = poll[1]; + int step = map.get(dx * n + dy); + if (grid[dx][dy] == 1) return step; + for (int[] di : dirs) { + int nx = dx + di[0], ny = dy + di[1]; + if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue; + int key = nx * n + ny; + if (map.containsKey(key)) continue; + d.addLast(new int[]{nx, ny}); + map.put(key, step + 1); + } + } + return -1; + } +} +``` +* 时间复杂度:$O(n^4)$ +* 空间复杂度:$O(n^2)$ + +--- + +### 多源 BFS + +这其实还是道「多源 BFS」入门题。 + +**与「单源最短路」不同,「多源最短路」问题是求从「多个源点」到达「一个/多个汇点」的最短路径。** + +在实现上,最核心的搜索部分,「多源 BFS」与「单源 BFS」并无区别。 + +**并且通过建立「虚拟源点」的方式,我们可以「多源 BFS」转换回「单源 BFS」问题。** + +什么意思? + +以本题为例,题面要我们求每个「海洋」区域到最近的「陆地」区域的最大值。 + +我们可以将「源点/起点」和「汇点/终点」进行反转:**从每个「陆地」区域出发,多个「陆地」区域每次同时向往扩散一圈,每个「海洋」区域被首次覆盖时所对应的圈数,就是「海洋」区域距离最近的「陆地」区域的距离。** + +![](https://files.mdnice.com/user/9208/c07cba06-567e-4ac6-a52d-38785687c8fb.png) + +不过,这是如何与「单源 BFS」联系起来的呢? + +我们可以想象存在一个「虚拟源点」,其与所有「真实源点」(陆地)存在等权的边,那么任意「海洋」区域与「最近的陆地」区域的最短路等价于与「虚拟源点」的最短路: + +![](https://files.mdnice.com/user/9208/0a9c7af5-6944-4844-94b5-5b82eb1dd0e6.png) + +**实现上,我们并不需要真的将这个虚拟源点建立出来,只需要将所有的「真实源点」进行入队即可。** + +这个过程相当于从队列中弹出「虚拟源点」,并把它所能到点(真实源点)进行入队,然后再进行常规的 BFS 即可。 + +一些细节:实现上为了方便,在进行常规 BFS 时,如果一个「海洋」区域被访问到,说明其被离它「最近的陆地」覆盖到了,修改值为最小距离。这样我们只需要考虑那些值仍然为 $0$ 的「海洋」区域即可(代表尚未被更新)。 + +代码: +```Java +class Solution { + public int maxDistance(int[][] grid) { + int n = grid.length; + Deque d = new ArrayDeque<>(); + Map map = new HashMap<>(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + d.add(new int[]{i, j}); + map.put(i * n + j, 0); + } + } + } + int ans = -1; + int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}}; + while (!d.isEmpty()) { + int[] poll = d.poll(); + int dx = poll[0], dy = poll[1]; + int step = map.get(dx * n + dy); + for (int[] di : dirs) { + int nx = dx + di[0], ny = dy + di[1]; + if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue; + if (grid[nx][ny] != 0) continue; + grid[nx][ny] = step + 1; + d.add(new int[]{nx, ny}); + map.put(nx * n + ny, step + 1); + ans = Math.max(ans, step + 1); + } + } + return ans; + } +} +``` +* 时间复杂度:$O(n^2)$ +* 空间复杂度:$O(n^2)$ + +--- + +### 总结 + +今天我们介绍了「多源 BFS」,通过建立「虚拟源点」,我们可以将其转化回「单源 BFS」问题。 + +实现上我们只需要将所有的「真实源点」进行入队,然后再进行 BFS 即可。 + +看起来两者区别不大,但其本质是通过源点/汇点转换,应用常规的 Flood Fill 将多次朴素 BFS 转化为一次 BFS,可以有效降低我们算法的时间复杂度。 + +--- + +### 最后 + +这是我们「刷穿 LeetCode」系列文章的第 `No.1162` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完。 + +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 + +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 + +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 \ No newline at end of file