【LeetCode】墙与门(BFS)
题目:
你被给定一个 m × n 的二维网格,网格中有以下三种可能的初始化值:
-1 表示墙或是障碍物
0 表示一扇门
INF 无限表示一个空的房间。然后,我们用 231 - 1 = 2147483647 代表 INF。你可以认为通往门的距离总是小于 2147483647 的。
你要给每个空房间位上填上该房间到 最近 门的距离,如果无法到达门,则填 INF 即可。
示例:
给定二维网格:
INF -1 0 INF
INF INF INF -1
INF -1 INF -1
0 -1 INF INF
运行完你的函数后,该网格应该变成:
3 -1 0 1
2 2 1 -1
1 -1 2 -1
0 -1 3 4
思路:
实际上就是找每个房间到最近的门的距离,我们从每个门开始,广度优先搜索并记录层数就行了。如果某个房间之前被标记过距离,那就选择这个距离和当前距离中较小的那个。这题要注意剪枝,如果下一步是门或者下一步是墙或者下一步已经访问过了,就不要加入队列中。否则会超时。
我使用的是利用队列进行BFS广度优先遍历,把门(0)做为起点加入到队列中,然后再把周围可到达的房间加入到队列中。
(1)剪枝:
1.判断是否有数组越界
2.判断是否有重复的遍历(使用HashSet集合)
3.判断这个数组下标的单元下存储的数字是否大于0
(2)剪枝:
判断之前被其它门搜索过的房间步长与现在的这个门的步长谁更佳的短!
代码如下:
public static void wallsAndGates(int[][] rooms) {
if(rooms.length==0||rooms[0].length==0||rooms==null)
return ;
for(int i=0;i<rooms.length;i++)
for(int j=0;j<rooms[i].length;j++)
if(rooms[i][j]==0)
BFS(rooms,i,j);
}
public static void BFS(int[][] r,int i,int j) {
Queue<Integer> queue = new LinkedList<Integer>();
Set<Integer> set = new HashSet<Integer>();
int dist = 0;
//这里的存储方式很好,不仅确保了数据唯一性,这样也便于后面的拆分!
queue.add(i*r[i].length+j);
set.add(i*r[i].length+j);
int size;
while(!queue.isEmpty()) {
size=queue.size();
for(int k=0;k<size;k++) {
int target = queue.poll();
int row = target/r[i].length;
int col = target%r[i].length;
//剪枝(2)
if(r[row][col]<=dist&&!(r[row][col]==0))
continue;
r[row][col] = Math.min(r[row][col],dist);
int up = row-1;
int down = row+1;
int left = col-1;
int right = col+1;
//剪枝(1)
if(up>=0&&r[up][col]>0&&!(set.contains(up*r[i].length+col))) {
queue.add(up*r[i].length+col);
set.add(up*r[i].length+col);
}
if(down<r.length&&r[down][col]>0&&!(set.contains(down*r[i].length+col))) {
queue.add(down*r[i].length+col);
set.add(down*r[i].length+col);
}
if(left>=0&&r[row][left]>0&&!(set.contains(row*r[i].length+left))) {
queue.add(row*r[i].length+left);
set.add(row*r[i].length+left);
}
if(right<r[row].length&&r[row][right]>0&&!(set.contains(row*r[i].length+right))) {
queue.add(row*r[i].length+right);
set.add(row*r[i].length+right);
}
}
dist++;
}
}
最重要的是用队列实现BFS的核心思想,还有进行剪枝(减少不必要的执行次数)。