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
class Program {
public int[] dijkstrasAlgorithm(int start, int[][][] edges) {
int numberOfVertex = edges.length;

int[] minDistances = new int[numberOfVertex];
Arrays.fill(minDistances, Integer.MAX_VALUE);
minDistances[start] = 0;

Set<Integer> visited = new HashSet<Integer>();

while (visited.size() != numberOfVertex) {
int[] currentVertexData = getVertexWithMinDistances(minDistances, visited);
int currentVertexIdx = currentVertexData[0];
int currentVertexDistanceToStart = currentVertexData[1];

if (currentVertexDistanceToStart == Integer.MAX_VALUE) {
break;
}

visited.add(currentVertexIdx);

// 来看从当前位置到自己所有neighors的距离是不是更近
for (int[] edge : edges[currentVertexIdx]) {
int destination = edge[0];
int distanceToDestination = edge[1];

// 之间就visit过(作为我们研究的对象, 把周围的neighbor都遍历了一遍并更新minDistance这个数组)
if (visited.contains(destination)) {
continue;
}

// 未被visited过的node可能从之前我们研究的某个node也能到达那里, 但是先到的不一定就更近
// 当然也有之前没有去过的node. 我们看看从当前的位置到周围的neighbor是不是更近
int newPathToDestinationDistance = currentVertexDistanceToStart + distanceToDestination;
if (newPathToDestinationDistance < minDistances[destination]) {
minDistances[destination] = newPathToDestinationDistance;
}
}
}

int[] finalDistances = new int[numberOfVertex];
for (int i = 0; i < numberOfVertex; i++) {
finalDistances[i] = minDistances[i] == Integer.MAX_VALUE ? -1 : minDistances[i];
}
return finalDistances;
}

public int[] getVertexWithMinDistances(int[] minDistances, Set<Integer> visited) {
int vertex = -1;
int currentMinDistance = Integer.MAX_VALUE;

for (int vertexIdx = 0; vertexIdx < minDistances.length; vertexIdx++) {
if (visited.contains(vertexIdx)) {
continue;
}
int currentDistance = minDistances[vertexIdx];
if (currentDistance <= currentMinDistance) {
vertex = vertexIdx;
currentMinDistance = currentDistance;
}
}

return new int[] { vertex, currentMinDistance };
}
}

AlgoExpert的写法. 大致思路就是我们用一个数组叫做min来记录到每个node的最短距离是多少. 一开始把这个数组初始化为Integer.MAX_VALUE. 因为可能有些node到达不了. 然后我们把start在min中的位置标记为0, 表示到start所需要的距离是0. 然后从这个node开始, 看start周围的neighbor, 到它们的距离是多少, 然后更新这个min, 把neighbors在min中的位置的内容都更新一下. 当我们把neighbors都遍历完后, 我们看看现在min中去下一个node哪个最近, 由于此时min中肯定是start最小, 是0, 因此我们用一个visited的set来存去过的node. 我们去到除了start外最近的node. 然后看它的neighbor, 此时start肯定是它的neighbor, 但是visited过了, 这说明到start的距离不可能更短了, 于是这一步visited的跳过. 我们会遇到有的neighor同样也是start的neighbor, 那是从这里到那个neighbor更近还是从start更近呢? 看一下比较一下就行了, 如果这里更近, 那就更新一下min即可. 然后我们再看min中没去过的node中最近的是哪一个. 这里的近指的是从start出发到哪个node更近. 重复上面的过程, 直到visit完所有的node, 或者发现min中没有被visite的node中最小的距离是Integer.MAX_VALUE, 这说明这个node不能到达, 其他有这个值的node也不能到达. 此时就可以brek了.

此时可以总结一下, 这个算法是按照距离start由近到远的距离去visit nodes. 在visit一个node的时候, 我们更新该node的neighbors在min中的信息. 被之前visited的neighbor不需要更新, 因为到达当前node前, 这个neighbor就已经被visited过, 这说明从start到该neighor更近, 如果在这里去neighbor, 首先我们要到这里, 此时距离已经比直接从start到neighbor要远, 更别说还要从这里到neighbor, 又有距离, 因此被visited过的neighbor不需要更新. 每次visit下一个node的时候, 找min中没被visit过的nodes中距离start最近的去visit. 如果此时未被visited过的node的最短距离是Intger.MAX_VALUE, 这表示这个node不能被到达. 其他有同样值的node也不能被到达. 最后我们根据min的信息来生成finalDistance.

Min Heap(PQ)做法

还有用min heap的做法, 具体见LC 743题. 简单来说就是把neighbor都装到min heap里面, 到我们要visit一个node的时候, 如果visit过, 直接continue, 这说明之前有更近的路线到达过这个node. 我们此时到达的路线太远, 直接忽略.

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
class Solution {
public int networkDelayTime(int[][] times, int N, int K) {

Map<Integer, Map<Integer, Integer>> graph = new HashMap<>();
for (int[] time : times) {
int from = time[0], to = time[1], dist = time[2];
graph.computeIfAbsent(from, k -> new HashMap<>()).put(to, dist);
}

Queue<int[]> que = new PriorityQueue<>((a, b) -> (a[1] - b[1]));
Set<Integer> visited = new HashSet<>();
que.offer(new int[]{K, 0});
int count = 0, res = 0;

while (!que.isEmpty()) {
int[] cur = que.poll();
int node = cur[0], dist = cur[1];
if (visited.contains(node)) { continue; }
count++;
res = dist;
if (count == N) break;
visited.add(node);
if (!graph.containsKey(node)) { continue; }
for (Map.Entry<Integer, Integer> next : graph.get(node).entrySet()) {
// 这一行可要可不要. 不要的原因是计算时间复杂度的时候更好计算.
if (visited.contains(next.getKey())) continue;
que.offer(new int[]{next.getKey(), next.getValue() + dist});
}
}

return count == N ? res : -1;
}
}

这个是模板.