[leetCode]1036. 逃离大迷宫
广度优先搜索
class Solution {
// 定义一个方向数组
static int[][] directions = new int[][]{{0, 1}, {1, 0}, {-1, 0},{0, -1}};
// X,Y的上限
static int limit = (int)1e6;
public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) {
if (blocked.length == 0) return true;
// 使用一个哈希表,保存被封锁的网格
Set<String> blocks = new HashSet<>();
for (int[] block : blocked) {
blocks.add(block[0] + ":" + block[1]);
}
/* 因为只迭代20000步, 如果是目标点在被封锁的区域内正着跑也可以到
20000步但是到不了目标点, 正反都跑一次才保证能到达目标点*/
// 目标的有可能被全部包围
return bfs(source, target, blocks) && bfs(target, source, blocks);
}
public boolean bfs(int[] source, int[] target, Set<String> blocks) {
// 使用一个哈希表保存已经走过的节点
Set<String> seen = new HashSet<>();
seen.add(source[0] + ":" + source[1]);
// 使用一个队列保存当前能走的网格坐标
Queue<int[]> queue = new LinkedList<>();
queue.add(source);
while (!queue.isEmpty()) {
// 当前坐标
int[] cur = queue.poll();
// 遍历方向数组directions
for (int[] dir : directions) {
// (nextX, nextY)为下一个网格点坐标
int nextX = dir[0] + cur[0];
int nextY = dir[1] + cur[1];
// 如果坐标越界跳过这个方向
if (nextX < 0 || nextY < 0 || nextX > limit || nextY > limit) continue;
// 用于哈希表查询,如果该坐标已经走过或者被封锁则走下一个方向
String key = nextX + ":" + nextY;
if (seen.contains(key) || blocks.contains(key)) continue;
// 走到了目标点返回true
if (nextX == target[0] && nextY == target[1]) return true;
queue.offer(new int[]{nextX, nextY});
// 添加到已走过的点中
seen.add(key);
}
// 因为 blocked 的 length 是 200
// 如果使用这 200 个 block 可以围成最大的区域是 19900,如下:
/*
0th _________________________
|O O O O O O O X
|O O O O O O X
|O O O O O X
|O O O O X
.O O O X
.O O X
.O X
200th |X
从上面可以计算出 block(即 X)可以围城的最大区域(是一个角的三角形),大小计算如下:
1 + 2 + 3 + 4 + ... + 199 = (1 + 199) * 199 / 2 = 19900
这里我们向上取整为 20000。
*/
// 也就是说,如果迭代了 20000 步还能继续走的话,那么是肯定可以到达 target 的
if (seen.size() > 19900) return true;
}
return false;
}
}