leetcode-542

题目链接

https://leetcode-cn.com/problems/01-matrix/

一波三折的解题

初看题目,这好像可以单源最短路径来解决。

初始思路

虽然一开始感觉可以用单源最短路径解决,但转念一想,好像并不需要这么麻烦,直接BFS解决也是可以的,就是找所有1到最近的0的距离么,BFS刚刚好。

设任意点的值为v,这个题目下的值只有0或1;任意点的距离值即答案为d;

然后整理下流程:

  1. 先找到01矩阵中v为1的点,将该点放入队列中。
  2. 然后从队列中拿出一个节点,用BFS的方式找出最近的一个v为0的点,并记录下距离d。
  3. 最后返回结果。

思路确实简单,而且也没啥毛病。但是我在BFS的时候用的是优先队列。事后我在想的时候,发现自己真是脑抽。但当时我是这样想的,对于当前的点,每一次把周围的四个点放到队列中,应该将距离值最小的点放到前面去,即优先弹出d小的点。其实,这是多此一举。因为先进入队列的都是d小的,后进入的肯定比前面进入的d至少多1。

此处还是贴上我心酸的代码

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
* 节点类
* 保存在矩阵中的索引号和距离值
* 实现比较器——优先队列需要
*/
public class Node implements Comparable<Node>{
private int x;
private int y;
private int val;

public Node(int x, int y) {
this.x = x;
this.y = y;
val = 0;
}

public int getX() {
return x;
}

public void setX(int x) {
this.x = x;
}

public int getY() {
return y;
}

public void setY(int y) {
this.y = y;
}

public int getVal() {
return val;
}

public void setVal(int val) {
this.val = val;
}

@Override
public int compareTo(Node o) {
return this.val - o.getVal();
}
}

/**
* 主类
*/
public class Solution {

public int[][] updateMatrix(int[][] matrix) {
int row = matrix.length;
int column = matrix[0].length;
int[][] ans = new int[row][column];
Queue<Node> queue = new LinkedList<>();

// 将1压入队列
for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
if (matrix[i][j] == 1) queue.add(new Node(i,j));
}
}

while (!queue.isEmpty()) {
bfs(queue.remove(),ans,matrix,row,column);
}

return ans;
}

private void bfs(Node start, int[][] ans,int[][] matrix, int row,int column) {
boolean[][] visit = new boolean[row][column];
PriorityQueue<Node> queue = new PriorityQueue<>();
visit[start.getX()][start.getY()] = true;
queue.add(start);

int[][] directions = {{0,-1},{-1,0},{0,1},{1,0}};
int nextX,nextY;
while (!queue.isEmpty()) {
Node cur = queue.remove();
if (matrix[cur.getX()][cur.getY()] == 0) {
ans[start.getX()][start.getY()] = cur.getVal();
return;
}

for (int i = 0; i < 4; i++) {
nextX = cur.getX()+directions[i][0];
nextY = cur.getY()+directions[i][1];
if (nextX >= 0 && nextX < row && nextY >= 0 && nextY < column && !visit[nextX][nextY]) {
visit[nextX][nextY] = true;
Node nextNode = new Node(nextX,nextY);
nextNode.setVal(cur.getVal()+1);
queue.add(nextNode);
}
}
}
}
}

结果:提交后超时。我想原因应该是优先队列的多此一举和重复遍历。

其中重复遍历是这个意思:

对于已知道距离d2的点m,如果当时的点为n,距离值为d1,BFS后遇到了m,将m放进队列时给这个点的距离值设置为d1+1。但m点是已知距离值的,即d1+d2这个距离就可以遇到一个v=0的点,这个d1+d2是可以利用的,这个值可能就是答案,也可能不是。

举个例子:

如这样的01矩阵: 图1

当程序进行到这里的时候:图2

此时,队列弹出的点是图2中的灰色框,那么按照BFS遍历,这个点的下一层为下图所示:

图3

可以发现,进入队列中的有三个点,但是其中2个点都是有值,按照一般流程会在进行下一层的遍历,这样其实会浪费一些时间,而我们可以利用两个已知值的点省略掉一些遍历。

对于已知距离值d的点,我们在进行BFS遍历到该点时就可以不需要在遍历该店的周围的点,如果遍历了,那就是重复遍历。

最终的思路

经过一番脑细胞死亡的过程,我想到最终AC的思路。

简而言之,就是用已知距离值的点推出未知距离值的点.

流程如下:

  1. 队列首先压入v=0且d=0的点,这些点就是初始时v=0的点。我们可以将这些点称为第一层的点。
  2. 之后,弹出上一层的每一个点,并对每个点进行BFS遍历,如果遍历到v=1的点且之前没有遍历过,计算距离值d,压入队列,这些点记为第二层的店。可以确定的是,对于每个遍历到的v=1的点x,其d的值都是最优的值(这个用反证法就很容易证明出来)。第二层的点的d都为1.
  3. 之后,重复2步骤,直到队列中没有节点。返回结果。

贴上代码:

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
57
58
59
60
61
62
63
64
public class Solution {

public int[][] updateMatrix(int[][] matrix) {
int row = matrix.length;
int column = matrix[0].length;
int[][] ans = new int[row][column];
boolean[][] visit = new boolean[row][column];
Queue<Node> queue = new LinkedList<>();

for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
if (matrix[i][j] == 0) {
queue.add(new Node(i,j));
visit[i][j] = true;
}
}
}

int[][] directions = {{0,-1},{-1,0},{0,1},{1,0}};
int nextX,nextY;
while (!queue.isEmpty()) {
Node cur = queue.remove();

for (int i = 0; i < 4; i++) {
nextX = cur.getX()+directions[i][0];
nextY = cur.getY()+directions[i][1];
if (nextX >= 0 && nextX < row && nextY >= 0 && nextY < column && !visit[nextX][nextY]) {
visit[nextX][nextY] = true;
Node nextNode = new Node(nextX,nextY);
ans[nextX][nextY] = cur.getVal()+1;
queue.add(nextNode);
}
}
}

return ans;
}
}

public class Node {
private int x;
private int y;

public Node(int x, int y) {
this.x = x;
this.y = y;
}

public int getX() {
return x;
}

public void setX(int x) {
this.x = x;
}

public int getY() {
return y;
}

public void setY(int y) {
this.y = y;
}
}