package com.freetymekiyan.algorithms.level.medium;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
/**
* You are given a m x n 2D grid initialized with these three possible values.
* <p>
* 1. -1 - A wall or an obstacle.
* 2. 0 - A gate.
* 3. INF - Infinity means an empty room. We use the value 2^31 - 1 = 2147483647 to represent INF as you may assume that
* the distance to a gate is less than 2147483647.
* <p>
* Fill each empty room with the distance to its nearest gate. If it is impossible to reach a gate, it should be filled
* with INF.
* <p>
* For example, given the 2D grid:
* | INF -1 0 INF
* | INF INF INF -1
* | INF -1 INF -1
* | 0 -1 INF INF
* <p>
* After running your function, the 2D grid should be:
* | 3 -1 0 1
* | 2 2 1 -1
* | 1 -1 2 -1
* | 0 -1 3 4
* Company Tags: Google, Facebook
* Tags: Breadth-first Search
* Similar Problems: (M) Surrounded Regions, (M) Number of Islands, (H) Shortest Distance from All Buildings
*/
public class WallsAndGates {
private static final int EMPTY = Integer.MAX_VALUE;
private static final int GATE = 0;
private static final int WALL = -1;
private static final int[][] DIRS = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
private WallsAndGates w;
/**
* BFS.
* Search from gate to rooms.
* As its breadth first search, it makes sure that:
* The rooms are visited level by level.
* So that rooms at distance 1 won't be visited until all gates are visited.
* Also use EMPTY room to indicate whether a room has been visited.
* <p>
* Implementations:
* Add all gates to the queue first (first level).
* Update its 4 adjacent empty rooms with new distance.
* Then add these rooms to the queue.
* Stop when the queue is empty.
*/
public void wallsAndGates(int[][] rooms) {
Queue<int[]> queue = new LinkedList<>();
// Add all zeros(gates) to queue.
for (int i = 0; i < rooms.length; i++) {
for (int j = 0; j < rooms[i].length; j++) {
if (rooms[i][j] == GATE) {
queue.offer(new int[]{i, j});
}
}
}
while (!queue.isEmpty()) {
int[] pos = queue.poll();
int r = pos[0];
int c = pos[1];
// Visit 4 adjacent nodes that are empty.
if (r > 0 && rooms[r - 1][c] == EMPTY) {
rooms[r - 1][c] = rooms[r][c] + 1; // Update distance before enqueue.
queue.offer(new int[]{r - 1, c});
}
if (r < rooms.length - 1 && rooms[r + 1][c] == EMPTY) {
rooms[r + 1][c] = rooms[r][c] + 1;
queue.offer(new int[]{r + 1, c});
}
if (c > 0 && rooms[r][c - 1] == EMPTY) {
rooms[r][c - 1] = rooms[r][c] + 1;
queue.offer(new int[]{r, c - 1});
}
if (c < rooms[0].length - 1 && rooms[r][c + 1] == EMPTY) {
rooms[r][c + 1] = rooms[r][c] + 1;
queue.offer(new int[]{r, c + 1});
}
}
}
/**
* BFS. Level-order Traversal.
* Use queue size to pull each level.
*/
public void wallsAndGatesB(int[][] rooms) {
Queue<int[]> queue = new LinkedList<>();
// Add all zeros to queue.
for (int i = 0; i < rooms.length; i++) {
for (int j = 0; j < rooms[i].length; j++) {
if (rooms[i][j] == GATE) {
queue.offer(new int[]{i, j});
}
}
}
while (!queue.isEmpty()) {
for (int i = queue.size(); i > 0; i--) { // Level-order.
int[] pos = queue.poll();
for (int j = 0; j < DIRS.length; j++) {
int nextI = pos[0] + DIRS[j][0];
int nextJ = pos[1] + DIRS[j][1];
if (0 <= nextI && nextI < rooms.length
&& 0 <= nextJ && nextJ < rooms[0].length
&& rooms[nextI][nextJ] == Integer.MAX_VALUE) {
rooms[nextI][nextJ] = rooms[pos[0]][pos[1]] + 1;
queue.offer(new int[]{nextI, nextJ});
}
}
}
}
}
@Before
public void setUp() {
w = new WallsAndGates();
}
@Test
public void testExamples() {
int[][]
input =
{{2147483647, -1, 0, 2147483647}, {2147483647, 2147483647, 2147483647, -1},
{2147483647, -1, 2147483647, -1}, {0, -1, 2147483647, 2147483647}};
w.wallsAndGates(input);
for (int[] row :
input) {
System.out.println(Arrays.toString(row));
}
}
@After
public void tearDown() {
w = null;
}
}