方法
这段代码是为了解决一个特定的问题:在一个由 0 和 1 组成的二维网格中找到从左上角到右下角的最短路径,同时允许在路径中最多消除 k 个障碍物(由 1 表示)。下面是对这段代码的详细解释:
- 方向数组(directions):
这是一个二维数组,表示四个可能的移动方向:上(-1,0)、下(1,0)、左(0,-1)和右(0,1)。
- 函数 shortestPath:
如果 k 大于或等于 m + n - 3,则直接返回 m + n - 2。这是因为在没有障碍的情况下,到达右下角需要的最少步数是 m + n - 2,如果 k 足够大,可以直接越过所有障碍物。
- 状态标记数组(flag):
boolean[][][] flag 用来标记在给定位置 (m, n) 时,剩余可消除障碍物的机会数 k 的状态是否已经被访问过。
- 广度优先搜索(BFS):
使用队列 q 进行广度优先搜索。 初始状态为 {0, 0, k},表示从 (0, 0) 开始,有 k 个障碍物可以消除。 在搜索的每一步,我们检查四个方向,并对每个方向进行以下操作: 确保新的位置没有越界。
如果下一个位置不是障碍物,并且这个状态没有被访问过,将其添加到队列中。
如果下一个位置是障碍物,且当前还有移除障碍物的机会,且这个状态没有被访问过,也将其添加到队列中。
- 寻找路径并返回结果:
在 BFS 过程中,如果到达了右下角 (m-1, n-1),则返回当前步数 step 作为最短路径的长度。 如果搜索结束后仍未到达右下角,则返回 -1,表示没有找到合适的路径。
class Solution {
public int shortestPath(int[][] grid, int k) {
int m = grid.length, n = grid[0].length;
// 如果k足够大以至于可以直接穿越所有障碍物,则直接返回最短路径长度
if (k > m + n - 2) {
return m + n - 2;
}
// 方向
int[] dirM = new int[]{-1, 1, 0, 0}; // up down left right
int[] dirN = new int[]{0, 0, -1, 1};
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{0, 0, k});
// 在位置 (m, n) 时,剩余可消除障碍物的机会数 k 的状态是否已经被访问过
boolean[][][] visited = new boolean[m][n][k + 1];
visited[0][0][k] = true;
int res = 0;
while (!queue.isEmpty()) {
res++;
int size = queue.size();
for (int i = 0; i < size; i++) {
int[] arr = queue.poll();
int curM = arr[0];
int curN = arr[1];
int curK = arr[2];
for (int j = 0; j < 4; j++) {
int nextM = curM + dirM[j];
int nextN = curN + dirN[j];
if (nextM >= 0 && nextM < m && nextN >= 0 && nextN < n) {
// 如果下一个位置是空的且未访问过
if (grid[nextM][nextN] == 0 && !visited[nextM][nextN][curK]) {
// 到达右下角
if (nextM == m - 1 && nextN == n - 1) {
return res;
}
visited[nextM][nextN][curK] = true;
queue.offer(new int[]{nextM, nextN, curK});
} else if (grid[nextM][nextN] == 1 && curK > 0 &&!visited[nextM][nextN][curK - 1]) {
//如果下一个位置是障碍物且还有消除机会
visited[nextM][nextN][curK - 1] = true;
queue.offer(new int[]{nextM, nextN, curK - 1});
}
}
}
}
}
return -1;
}
}