# 一.Dijkstra
# 1.Dijkstra 简介
Edsger Wybe Dijkstra
艾兹格·维布·迪克斯特拉(Edsger Wybe Dijkstra,/ˈdaɪkstrə/ DYKE-strə;荷兰语:[ˈɛtsxər ˈʋibə ˈdɛikstra] 1930 年 5 月 11 日-2002 年 8 月 6 日)是一位荷兰计算机科学家、程序员、软件工程师、系统科学家和科学散文家。他因对开发结构化编程语言做出的基础贡献而获得了 1972 年的图灵奖,并担任德克萨斯大学奥斯汀分校的斯伦贝谢百年计算机科学主席,任职时间从 1984 年到 2000 年。在他于 2002 年去世前不久,他因其在程序计算的自稳定性方面的工作而获得了 ACM PODC 分布式计算有影响力论文奖。为了纪念他,该年度奖项在接下来的一年更名为迪克斯特拉奖。
迪克斯特拉在计算机科学领域的贡献
- 最短路径算法,也称为迪克斯特拉算法,现代计算机科学本科课程中广泛教授
- Shunting yard 算法
- THE OS 操作系统
- 银行家算法
- 用于协调多个处理器和程序的信号量构造
- 在分布式计算领域提出概念:自稳定性
# 2.有向无环图
graph LR
1--7-->2
1--9--->3
1--14--->6
6--9--->5
3--2--->6
2--15--->4
3--11--->4
4--6--->5
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 3.算法描述
算法描述:
- 将所有顶点标记为未访问。创建一个未访问顶点的集合。
- 为每个顶点分配一个临时距离值
- 对于我们的初始顶点,将其设置为零
- 对于所有其他顶点,将其设置为无穷大。
- 每次选择最小临时距离的未访问顶点,作为新的当前顶点
- 对于当前顶点,遍历其所有未访问的邻居,并更新它们的临时距离为更小
- 例如,1->6 的距离是 14,而 1->3->6 的距离是 11。这时将距离更新为 11
- 否则,将保留上次距离值
- 当前顶点的邻居处理完成后,把它从未访问集合中删除
# 4.java 实现
public class Dijkstra {
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
v1.edges = List.of(new Edge(v3, 9), new Edge(v2, 7), new Edge(v6, 14));
v2.edges = List.of(new Edge(v4, 15));
v3.edges = List.of(new Edge(v4, 11), new Edge(v6, 2));
v4.edges = List.of(new Edge(v5, 6));
v5.edges = List.of();
v6.edges = List.of(new Edge(v5, 9));
List<Vertex> graph = List.of(v1, v2, v3, v4, v5, v6);
dijkstra(graph, v1);
}
private static void dijkstra(List<Vertex> graph, Vertex source) {
ArrayList<Vertex> list = new ArrayList<>(graph);
source.dist = 0;
while (!list.isEmpty()) {
// 3. 选取当前顶点
Vertex curr = chooseMinDistVertex(list);
// 4. 更新当前顶点邻居距离
updateNeighboursDist(curr, list);
// 5. 移除当前顶点
list.remove(curr);
}
for (Vertex v : graph) {
System.out.println(v.name + " " + v.dist);
}
}
private static void updateNeighboursDist(Vertex curr, ArrayList<Vertex> list) {
for (Edge edge : curr.edges) {
Vertex n = edge.linked;
if (list.contains(n)) {
int dist = curr.dist + edge.weight;
if (dist < n.dist) {
n.dist = dist;
}
}
}
}
private static Vertex chooseMinDistVertex(ArrayList<Vertex> list) {
Vertex min = list.get(0);
for (int i = 1; i < list.size(); i++) {
if (list.get(i).dist < min.dist) {
min = list.get(i);
}
}
return min;
}
}
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
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
# 5.优化改进
改进 - 优先级队列
- 创建一个优先级队列,放入所有顶点(队列大小会达到边的数量)
- 为每个顶点分配一个临时距离值
- 对于我们的初始顶点,将其设置为零
- 对于所有其他顶点,将其设置为无穷大。
- 每次选择最小临时距离的未访问顶点,作为新的当前顶点
- 对于当前顶点,遍历其所有未访问的邻居,并更新它们的临时距离为更小,若距离更新需加入队列
- 例如,1->6 的距离是 14,而 1->3->6 的距离是 11。这时将距离更新为 11
- 否则,将保留上次距离值
- 当前顶点的邻居处理完成后,把它从队列中删除
public class DijkstraPriorityQueue {
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
v1.edges = List.of(new Edge(v3, 9), new Edge(v2, 7), new Edge(v6, 14));
v2.edges = List.of(new Edge(v4, 15));
v3.edges = List.of(new Edge(v4, 11), new Edge(v6, 2));
v4.edges = List.of(new Edge(v5, 6));
v5.edges = List.of();
v6.edges = List.of(new Edge(v5, 9));
List<Vertex> graph = List.of(v1, v2, v3, v4, v5, v6);
dijkstra(graph, v1);
}
private static void dijkstra(List<Vertex> graph, Vertex source) {
PriorityQueue<Vertex> queue = new PriorityQueue<>(Comparator.comparingInt(v -> v.dist));
source.dist = 0;
for (Vertex v : graph) {
queue.offer(v);
}
while (!queue.isEmpty()) {
System.out.println(queue);
// 3. 选取当前顶点
Vertex curr = queue.peek();
// 4. 更新当前顶点邻居距离
if(!curr.visited) {
updateNeighboursDist(curr, queue);
curr.visited = true;
}
// 5. 移除当前顶点
queue.poll();
}
for (Vertex v : graph) {
System.out.println(v.name + " " + v.dist + " " + (v.prev != null ? v.prev.name : "null"));
}
}
private static void updateNeighboursDist(Vertex curr, PriorityQueue<Vertex> queue) {
for (Edge edge : curr.edges) {
Vertex n = edge.linked;
if (!n.visited) {
int dist = curr.dist + edge.weight;
if (dist < n.dist) {
n.dist = dist;
n.prev = curr;
queue.offer(n);
}
}
}
}
}
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
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
# 6.存在问题
有负数边的情况
graph LR
v1 --2--> v2
v1 --1--> v3
v2 --"-2"--> v3
v3 --1--> v4
1
2
3
4
5
2
3
4
5
按照 Dijkstra 算法,得出
- v1 -> v2 最短距离 2
- v1 -> v3 最短距离 1
- v1 -> v4 最短距离 2
事实应当是
- v1 -> v2 最短距离 2
- v1 -> v3 最短距离 0
- v1 -> v4 最短距离 1
# 二.Bellman-Ford
# 1.java 实现
public class BellmanFord {
public static void main(String[] args) {
// 正常情况
/*Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
v1.edges = List.of(new Edge(v3, 9), new Edge(v2, 7), new Edge(v6, 14));
v2.edges = List.of(new Edge(v4, 15));
v3.edges = List.of(new Edge(v4, 11), new Edge(v6, 2));
v4.edges = List.of(new Edge(v5, 6));
v5.edges = List.of();
v6.edges = List.of(new Edge(v5, 9));
List<Vertex> graph = List.of(v4, v5, v6, v1, v2, v3);*/
// 负边情况
/*Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
v1.edges = List.of(new Edge(v2, 2), new Edge(v3, 1));
v2.edges = List.of(new Edge(v3, -2));
v3.edges = List.of(new Edge(v4, 1));
v4.edges = List.of();
List<Vertex> graph = List.of(v1, v2, v3, v4);*/
// 负环情况
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
v1.edges = List.of(new Edge(v2, 2));
v2.edges = List.of(new Edge(v3, -4));
v3.edges = List.of(new Edge(v4, 1), new Edge(v1, 1));
v4.edges = List.of();
List<Vertex> graph = List.of(v1, v2, v3, v4);
bellmanFord(graph, v1);
}
private static void bellmanFord(List<Vertex> graph, Vertex source) {
source.dist = 0;
int size = graph.size();
// 1. 进行 顶点个数 - 1 轮处理
for (int i = 0; i < size - 1; i++) {
// 2. 遍历所有的边
for (Vertex s : graph) {
for (Edge edge : s.edges) {
// 3. 处理每一条边
Vertex e = edge.linked;
if (s.dist != Integer.MAX_VALUE && s.dist + edge.weight < e.dist) {
e.dist = s.dist + edge.weight;
e.prev = s;
}
}
}
}
for (Vertex v : graph) {
System.out.println(v + " " + (v.prev != null ? v.prev.name : "null"));
}
}
}
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
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
# 2.负环
graph LR
v1 --2--> v2
v2 --"-4"--> v3
v3 --1--> v4
v3 --1--> v1
1
2
3
4
5
6
2
3
4
5
6
如果在【顶点-1】轮处理完成后,还能继续找到更短距离,表示发现了负环
# 三.Floyd-Warshall
# 1.负环图
graph LR
v1 --"-2"--> v3
v2 --"4"--> v1
v2 --"3"--> v3
v3 --2--> v4
v4 --"-1"--> v2
1
2
3
4
5
6
2
3
4
5
6
# 2.java 实现
public class FloydWarshall {
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
v1.edges = List.of(new Edge(v3, -2));
v2.edges = List.of(new Edge(v1, 4), new Edge(v3, 3));
v3.edges = List.of(new Edge(v4, 2));
v4.edges = List.of(new Edge(v2, -1));
List<Vertex> graph = List.of(v1, v2, v3, v4);
/*
直接连通
v1 v2 v3 v4
v1 0 ∞ -2 ∞
v2 4 0 3 ∞
v3 ∞ ∞ 0 2
v4 ∞ -1 ∞ 0
k=0 借助v1到达其它顶点
v1 v2 v3 v4
v1 0 ∞ -2 ∞
v2 4 0 2 ∞
v3 ∞ ∞ 0 2
v4 ∞ -1 ∞ 0
k=1 借助v2到达其它顶点
v1 v2 v3 v4
v1 0 ∞ -2 ∞
v2 4 0 2 ∞
v3 ∞ ∞ 0 2
v4 3 -1 1 0
k=2 借助v3到达其它顶点
v1 v2 v3 v4
v1 0 ∞ -2 0
v2 4 0 2 4
v3 ∞ ∞ 0 2
v4 3 -1 1 0
k=3 借助v4到达其它顶点
v1 v2 v3 v4
v1 0 -1 -2 0
v2 4 0 2 4
v3 5 1 0 2
v4 3 -1 1 0
*/
floydWarshall(graph);
}
static void floydWarshall(List<Vertex> graph) {
int size = graph.size();
int[][] dist = new int[size][size];
Vertex[][] prev = new Vertex[size][size];
// 1)初始化
for (int i = 0; i < size; i++) {
Vertex v = graph.get(i); // v1 (v3)
Map<Vertex, Integer> map = v.edges.stream().collect(Collectors.toMap(e -> e.linked, e -> e.weight));
for (int j = 0; j < size; j++) {
Vertex u = graph.get(j); // v3
if (v == u) {
dist[i][j] = 0;
} else {
dist[i][j] = map.getOrDefault(u, Integer.MAX_VALUE);
prev[i][j] = map.get(u) != null ? v : null;
}
}
}
print(prev);
// 2)看能否借路到达其它顶点
/*
v2->v1 v1->v?
dist[1][0] + dist[0][0]
dist[1][0] + dist[0][1]
dist[1][0] + dist[0][2]
dist[1][0] + dist[0][3]
*/
for (int k = 0; k < size; k++) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
// dist[i][k] + dist[k][j] // i行的顶点,借助k顶点,到达j列顶点
// dist[i][j] // i行顶点,直接到达j列顶点
if (dist[i][k] != Integer.MAX_VALUE &&
dist[k][j] != Integer.MAX_VALUE &&
dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
prev[i][j] = prev[k][j];
}
}
}
// print(dist);
}
print(prev);
}
static void path(Vertex[][] prev, List<Vertex> graph, int i, int j) {
LinkedList<String> stack = new LinkedList<>();
System.out.print("[" + graph.get(i).name + "," + graph.get(j).name + "] ");
stack.push(graph.get(j).name);
while (i != j) {
Vertex p = prev[i][j];
stack.push(p.name);
j = graph.indexOf(p);
}
System.out.println(stack);
}
static void print(int[][] dist) {
System.out.println("-------------");
for (int[] row : dist) {
System.out.println(Arrays.stream(row).boxed()
.map(x -> x == Integer.MAX_VALUE ? "∞" : String.valueOf(x))
.map(s -> String.format("%2s", s))
.collect(Collectors.joining(",", "[", "]")));
}
}
static void print(Vertex[][] prev) {
System.out.println("-------------------------");
for (Vertex[] row : prev) {
System.out.println(Arrays.stream(row).map(v -> v == null ? "null" : v.name)
.map(s -> String.format("%5s", s))
.collect(Collectors.joining(",", "[", "]")));
}
}
}
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# 3.负环
如果在 3 层循环结束后,在 dist 数组的对角线处(i==j 处)发现了负数,表示出现了负环