题目
多源BFS
public int[][] updateMatrix(int[][] matrix) {
// distance[i][j] 代表每个位置到0的最短距离
int [][] distance = new int[matrix.length][matrix[0].length];
boolean [][] visited = new boolean[matrix.length][matrix[0].length];
Queue<int []> queue = new LinkedList();
for (int i = 0; i < matrix.length; i ++) {
for (int j = 0; j < matrix[0].length; j ++) {
if (matrix[i][j] == 0) {
queue.offer(new int[]{i, j});
distance[i][j] = 0;
visited[i][j] = Boolean.TRUE;
}
}
}
int [] temp1 = new int[] {0, 0, 1, -1};
int [] temp2 = new int[] {1, -1, 0, 0};
while (!queue.isEmpty()) {
int size = queue.size();
// 每一次扩散 相当于所有的0向上下左右走了一步, 所有能被扩散到的点, 都是1
// 注意是相当于同时扩散, 能被扩散到的距离一定是 + 1
for (int i = 0; i < size; i ++) {
int [] origin = queue.poll();
for (int j = 0; j < 4; j ++) {
int targetRow = origin[0] + temp1[j];
int targetCol = origin[1] + temp2[j];
if (targetRow >=0 && targetRow < matrix.length && targetCol >= 0 && targetCol < matrix[0].length && !visited[targetRow][targetCol]) {
distance[targetRow][targetCol] = distance[origin[0]][origin[1]] + 1;
visited[targetRow][targetCol] = Boolean.TRUE;
queue.offer(new int[] {targetRow, targetCol});
}
}
}
}
return distance;
}
思路
(1) 目的是寻找1到最近0的距离, 假设我们以1为起点, 通过上下左右四个方向进行寻找, 然后下一轮将涉及到的4个节点添加到队列中, 作为下一轮的起点, 然后下一轮对于每个起点, 再次扩散上下左右, 只到遇到第一个0为止, 扩散的轮数, 就是距离.
但是这样的话, 我每次都只能以1为起点, 然后扩散一遍, 中间存在了很多重复计算根本无法用到, 例如大量的1到1的距离, 能帮助我们什么呢? 并且本身1到1自己的距离, 都没用, 只能通过轮数来计算距离
(2) 但是如果我们以0为起点, 寻找1呢, 根据题意0到0的距离是1, 因此0在扩散一次后, 每个位置(除来本身是0的以外)距离当前0的距离是可以计算得到的, 并且这个距离在一下扩散是可以用到的, 结果可以记录下来, 不用重复计算了. 如果当只有一个0的时候, 那么这个思路就够了.
(3) 如果有多个0的话, 哪个0先扩散就成了问题, 因此可以把所有0视为同一个0, 让大家一起扩散, 这样能够保证每个非0的元素 被扩散到的0, 一定是距离它最近的一个0之一. 因为如果一个1两边都是0, 就算0实际上是先后扩散的, 但是只扩散一步的情况下, 被左边的0扩散到, 和被右边的0扩散到, 结果是一样的.
通过同时扩散的方式, 就无需就最小距离了, 因为能被扩散到的 一定是最小距离