1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class Solution {
private int[][] DIRECTIONS = new int[][] { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };

public int shortestDistance(int[][] grid) {
int[][][] gridSumCount = new int[grid.length][grid[0].length][2];
Deque<int[]> buildings = new ArrayDeque<>();
int buildingCount = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
buildings.offer(new int[] { i, j });
buildingCount += 1;
}
}
}
while (!buildings.isEmpty()) {
int[] currBuilding = buildings.poll();
Deque<int[]> possibleSites = new ArrayDeque<>();
possibleSites.offer(currBuilding);
boolean[][] visited = new boolean[grid.length][grid[0].length];
visited[currBuilding[0]][currBuilding[1]] = true;
int steps = 1;
while (!possibleSites.isEmpty()) {
int size = possibleSites.size();
for (int i = 0; i < size; i++) {
int[] currPos = possibleSites.poll();
for (int[] direction : DIRECTIONS) {
int newRow = currPos[0] + direction[0];
int newCol = currPos[1] + direction[1];
if (!isOutOfBound(grid, newRow, newCol) && grid[newRow][newCol] == 0
&& !visited[newRow][newCol]) {
gridSumCount[newRow][newCol][0] += steps;
gridSumCount[newRow][newCol][1] += 1;
visited[newRow][newCol] = true;
possibleSites.offer(new int[] { newRow, newCol });
}
}
}
steps += 1;
}
}
int ans = Integer.MAX_VALUE;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 0 && gridSumCount[i][j][1] == buildingCount) {
ans = Math.min(ans, gridSumCount[i][j][0]);
}
}
}
return ans == Integer.MAX_VALUE ? -1 : ans;
}

private boolean isOutOfBound(int[][] grid, int row, int col) {
return row < 0 || row >= grid.length || col < 0 || col >= grid[0].length;
}
}

From building to empty spaces.

注意的是level traversal的时候, for循环条件不能写成i < possibleSites.size(), 因为每次for完后, possibleSites的size会变. 但是可以让i = possibleSites.size(); i > 0; i–这样. 这一点我找了好久.
淦!

时间复杂度: O(m^2 * n^2)
空间复杂度: O(m * n)

还可以更简洁, 就是把bfs封装成函数, 数building数目那里直接调用bfs即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
private final int[][] DIRECTIONS = new int[][] { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };

public int shortestDistance(int[][] grid) {
int targetSpotValue = 0;
int[][] total = new int[grid.length][grid[0].length];
int ans = Integer.MAX_VALUE;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
ans = Integer.MAX_VALUE;
Deque<int[]> emptySpots = new ArrayDeque<>();
emptySpots.offer(new int[] { i, j });
int steps = 1;
while (!emptySpots.isEmpty()) {
for (int k = emptySpots.size(); k > 0; k--) {
int[] currPos = emptySpots.poll();
for (int[] direction : DIRECTIONS) {
int newRow = currPos[0] + direction[0];
int newCol = currPos[1] + direction[1];
if (!isOutOfBound(grid, newRow, newCol) && grid[newRow][newCol] == targetSpotValue) {
total[newRow][newCol] += steps;
grid[newRow][newCol] -= 1;
emptySpots.offer(new int[] { newRow, newCol });
ans = Math.min(ans, total[newRow][newCol]);
}
}
}
steps += 1;
}
targetSpotValue -= 1;
}
}
}
return ans == Integer.MAX_VALUE ? -1 : ans;
}

private boolean isOutOfBound(int[][] grid, int row, int col) {
return row < 0 || row >= grid.length || col < 0 || col >= grid[0].length;
}
}

这是第二种解法. 对于每个1, 我们都要BFS. 那么我们就要标记visited过的地方. 如何标记呢? 我们就把visited过的地方减去1. 对于第一次BFS, 我们要找的就是是0的位置, 找到后把0变为-1然后在我们的额外二维数组中在对应位置加上目前的1到这个0需要的步数. 等到第二次BFS, 之前能被第一个BFS到达的地方都被标记上了-1, 于是我们就要找是-1的格子(是0的格子表示这个点不能到达第一个BFS的1, 因此它不可能为我们的答案, 我们的答案应该是所有1都能到达). 找到-1格子把它变为-2, 然后在total中对应位置更新距离. 这样就在原二维数组上标记去过的地方了.

同时我们也能在一次遍历后就得到答案, 做法就是在每次BFS的时候, 记录此次哪个位置目前的sum最小, 我们总会到最后一次BFS, 那么最后一次记录的ans就是答案, 之前的记录都不是答案, 只是最后一次BFS的答案是我们想要的.

时间复杂度: O(M^2N^2) 因为我们要对每个1都BFS, 一次BFS是O(MN), 一共MN个元素, 那就是O(M^2N^2)
空间复杂度: O(mn) 我们用total来存空地到每个1的sum是多少.