网格中的最短路径
难度:
标签:
题目描述
给你一个 m * n
的网格,其中每个单元格不是 0
(空)就是 1
(障碍物)。每一步,您都可以在空白单元格中上、下、左、右移动。
如果您 最多 可以消除 k
个障碍物,请找出从左上角 (0, 0)
到右下角 (m-1, n-1)
的最短路径,并返回通过该路径所需的步数。如果找不到这样的路径,则返回 -1
。
示例 1:
输入: grid = [[0,0,0],[1,1,0],[0,0,0],[0,1,1],[0,0,0]], k = 1
输出:6
解释:
不消除任何障碍的最短路径是 10。
消除位置 (3,2) 处的障碍后,最短路径是 6 。该路径是 (0,0) -> (0,1) -> (0,2) -> (1,2) -> (2,2) -> (3,2) -> (4,2)
.
示例 2:
输入:grid = [[0,1,1],[1,1,1],[1,0,0]], k = 1 输出:-1 解释:我们至少需要消除两个障碍才能找到这样的路径。
提示:
grid.length == m
grid[0].length == n
1 <= m, n <= 40
1 <= k <= m*n
grid[i][j]
是0
或1
grid[0][0] == grid[m-1][n-1] == 0
代码结果
运行时间: 43 ms, 内存: 16.7 MB
import java.util.*;
import java.util.stream.IntStream;
/**
* This method uses Java Streams to help with the BFS approach to find the shortest path in a grid with obstacles.
* It leverages streams for initializing and processing parts of the grid.
*/
public class Solution {
public int shortestPath(int[][] grid, int k) {
int m = grid.length;
int n = grid[0].length;
int[][][] visited = new int[m][n][k + 1];
IntStream.range(0, m).forEach(i -> IntStream.range(0, n).forEach(j -> Arrays.fill(visited[i][j], Integer.MAX_VALUE)));
int[] dirs = {0, 1, 0, -1, 0};
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{0, 0, 0, 0});
visited[0][0][0] = 0;
while (!queue.isEmpty()) {
int[] cur = queue.poll();
int x = cur[0], y = cur[1], steps = cur[2], obstacles = cur[3];
if (x == m - 1 && y == n - 1) {
return steps;
}
for (int i = 0; i < 4; i++) {
int nx = x + dirs[i];
int ny = y + dirs[i + 1];
if (nx >= 0 && ny >= 0 && nx < m && ny < n) {
int newObstacles = obstacles + grid[nx][ny];
if (newObstacles <= k && steps + 1 < visited[nx][ny][newObstacles]) {
visited[nx][ny][newObstacles] = steps + 1;
queue.offer(new int[]{nx, ny, steps + 1, newObstacles});
}
}
}
}
return -1;
}
}
解释
方法:
这题解使用了宽度优先搜索(BFS)来找到从网格的左上角到右下角的最短路径。这里的关键是可以选择消除最多k个障碍物。为了处理这个条件,搜索状态不仅包括当前位置坐标 (x, y),还包括一个剩余障碍物消除次数 `rest`。状态被保存在一个队列中,每个状态为 (x, y, rest),其中 `rest` 表示还可以消除的障碍物数量。为防止重复处理相同的状态,使用一个集合 `visited` 来记录已经访问过的状态。搜索过程中,对于每个状态,都尝试向四个方向移动,如果是空白格并且该状态未被访问,则将其加入队列;如果是障碍物且 `rest` 大于0,则消除障碍物并将新状态加入队列。如果能到达终点,则返回当前步数。
时间复杂度:
O(m*n*k)
空间复杂度:
O(m*n*k)
代码细节讲解
🦆
在算法中,如果起始位置`(0, 0)`或终点位置`(m-1, n-1)`本身就是障碍物,这种情况下该如何处理?
▷🦆
你在代码中提到如果`k >= m + n - 3`就可以直接返回`m + n - 2`,这个判断的依据是什么?为什么是`m + n - 3`而不是其他值?
▷🦆
在使用BFS处理时,为什么选择使用`(x, y, rest)`作为状态而不是只用`(x, y)`?这样做的优势和可能带来的问题是什么?
▷🦆
在尝试移动到下一个位置`(nx, ny)`时,你会检查`(nx, ny, rest)`或`(nx, ny, rest-1)`是否已被访问。这里的访问集合`visited`是如何确保算法效率的,同时它可能存在哪些潜在的问题?
▷