迪杰斯特拉(Dijkstra)算法基本介绍

应用场景

最短路径问题

image-202211281544229

  1. 现在有7个村庄(A,B,C,D,E,F,G),有6个邮差,从G点出发,需要把邮件分别送到A,B,C,D,E,F这6个村庄。
  2. 各个村庄的距离用边线表示(权),比如A-B距离5公里。
  3. 问:如何计算出G村庄到其它各个村庄的最短距离?
  4. 如果从其它点出发到各个点的最短距离又是多少?

基本介绍

迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其它节点的最短路径。它的主要特点是以起始点为中心向外层扩展(广度优先思想),直到扩展到终点为止。

算法过程

设置出发顶点为v,顶点集合V{v1,v2,vi…},v到V中各个顶点的距离构成距离集合Dis{d1,d2,di…},Dis集合记录着v到图中各顶点的距离(到自身可以看作0,v到vi距离对应位di);

  1. 从Dis中选择值最小的di并移出Dis集合,同时移出V集合中对应的顶点vi,此时的v到vi即为最短路径。
  2. 更新Dis集合,更新规则为:比较v到V集合中顶点的距离值,和v通过vi到V集合中顶点的距离值,保留值较小的一个(同时也应该更新顶点的前驱节点为vi,表明是通过vi到达的)。
  3. 重复执行以上两步,直到最短路径顶点为目标顶点即可结束。

Dijkstra算法思路图解

image-20221130114334341

1
2
3
4
5
class VisitedVertex{ // 访问顶点集合
public int[] already_arr; //顶点的访问情况(0代表未访问,1代表已访问),会动态更新;
public int[] dis; // 记录出发顶点到其他顶点的距离,会动态更新,求出最短距离又会存放到dis中。
public int[] pre_visited; // 每一个下标对应的值为前一个顶点下标,会动态更新。
}

Dijkstra算法解决最短路径问题-构建图

代码

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
package com.algorithm.dijkstra;

import java.util.Arrays;

/**
* @author Joker大雄
* @data 2022/11/30 - 11:51
**/
public class DijkstraAlgorithm {
public static void main(String[] args) {
char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
// 邻接矩阵
int[][] matrix = new int[vertex.length][vertex.length];
final int N = 65535; // 表示不可连通
matrix[0] = new int[]{N, 5, 7, N, N, N, 2};
matrix[1] = new int[]{5, N, N, 9, N, N, 3};
matrix[2] = new int[]{7, N, N, N, 8, N, N};
matrix[3] = new int[]{N, 9, N, N, N, 4, N};
matrix[4] = new int[]{N, N, 8, N, N, 5, 4};
matrix[5] = new int[]{N, N, N, 4, 5, N, 6};
matrix[6] = new int[]{2, 3, N, N, 4, 6, N};
// 创建Graph对象
Graph graph = new Graph(vertex, matrix);
graph.showGraph();
}
}

// 构建图
class Graph {
private char[] vertex;// 存放顶点
private int[][] matrix; //邻接矩阵

// 构造器
public Graph(char[] vertex, int[][] matrix) {
this.vertex = vertex;
this.matrix = matrix;
}

// 显示图
public void showGraph() {
for (int[] link : matrix) {
System.out.println(Arrays.toString(link));
}
}
}

运行结果

1
2
3
4
5
6
7
8
9
[65535, 5, 7, 65535, 65535, 65535, 2]
[5, 65535, 65535, 9, 65535, 65535, 3]
[7, 65535, 65535, 65535, 8, 65535, 65535]
[65535, 9, 65535, 65535, 65535, 4, 65535]
[65535, 65535, 8, 65535, 65535, 5, 4]
[65535, 65535, 65535, 4, 5, 65535, 6]
[2, 3, 65535, 65535, 4, 6, 65535]

Process finished with exit code 0

Dijkstra算法解决最短路径问题-关键方法构建

代码

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
package com.algorithm.dijkstra;

import java.util.Arrays;

/**
* @author Joker大雄
* @data 2022/11/30 - 11:51
**/
public class DijkstraAlgorithm {
public static void main(String[] args) {
char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
// 邻接矩阵
int[][] matrix = new int[vertex.length][vertex.length];
final int N = 65535; // 表示不可连通
matrix[0] = new int[]{N, 5, 7, N, N, N, 2};
matrix[1] = new int[]{5, N, N, 9, N, N, 3};
matrix[2] = new int[]{7, N, N, N, 8, N, N};
matrix[3] = new int[]{N, 9, N, N, N, 4, N};
matrix[4] = new int[]{N, N, 8, N, N, 5, 4};
matrix[5] = new int[]{N, N, N, 4, 5, N, 6};
matrix[6] = new int[]{2, 3, N, N, 4, 6, N};
// 创建Graph对象
Graph graph = new Graph(vertex, matrix);
graph.showGraph();
// 测试迪杰斯特拉算法
graph.dsj(6);
}
}

// 访问顶点集合
class VisitedVertex {
//顶点的访问情况(0代表未访问,1代表已访问),会动态更新;
public int[] already_arr;
// 记录出发顶点到其他顶点的距离,会动态更新,求出最短距离又会存放到dis中。
public int[] dis;
// 每一个下标对应的值为前一个顶点下标,会动态更新。
public int[] pre_visited;

// 构造器

/**
* @param len 顶点个数
* @param index 出发顶点的下标
*/
public VisitedVertex(int len, int index) {
this.already_arr = new int[len];
this.pre_visited = new int[len];
this.dis = new int[len];
// 初始化dis
Arrays.fill(dis, 65535);
already_arr[index] = 1; // 设置出发顶点被访问
this.dis[index] = 0; // 设置出发顶的访问距离为0
}

/**
* function:判断index顶点是否被访问过
*
* @param index
* @return 如果访问过返回true,否则返回false
*/
public boolean in(int index) {
return already_arr[index] == 1;
}

/**
* function:更新出发顶点到index顶点的距离
*
* @param index
* @param len
*/
public void updateDis(int index, int len) {
dis[index] = len;
}

/**
* function:更新pre顶点的前驱顶点为index的顶点
*
* @param pre
* @param index
*/
public void updatePre(int pre, int index) {
pre_visited[pre] = index;
}

/**
* function:返回出发顶点到index顶点的距离
*
* @param index
* @return
*/
public int getDis(int index) {
return dis[index];
}
}

// 构建图
class Graph {
private char[] vertex;// 存放顶点
private int[][] matrix; //邻接矩阵
private VisitedVertex vV; // 已经访问的顶点集合

// 构造器
public Graph(char[] vertex, int[][] matrix) {
this.vertex = vertex;
this.matrix = matrix;
}

// 显示图
public void showGraph() {
for (int[] link : matrix) {
System.out.println(Arrays.toString(link));
}
}

// 迪杰斯特拉算法
/**
*
* @param index 出发顶点对应的下标
*/
public void dsj(int index){
vV = new VisitedVertex(vertex.length, index);
}
}

运行结果

查看运行结果与思路图解里初始化的值,是否相同。

image-20221130124650557

Dijkstra算法解决最短路径问题-更新顶点

代码

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
130
131
132
133
134
135
136
137
138
139
package com.algorithm.dijkstra;

import java.util.Arrays;

/**
* @author Joker大雄
* @data 2022/11/30 - 11:51
**/
public class DijkstraAlgorithm {
public static void main(String[] args) {
char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
// 邻接矩阵
int[][] matrix = new int[vertex.length][vertex.length];
final int N = 65535; // 表示不可连通
matrix[0] = new int[]{N, 5, 7, N, N, N, 2};
matrix[1] = new int[]{5, N, N, 9, N, N, 3};
matrix[2] = new int[]{7, N, N, N, 8, N, N};
matrix[3] = new int[]{N, 9, N, N, N, 4, N};
matrix[4] = new int[]{N, N, 8, N, N, 5, 4};
matrix[5] = new int[]{N, N, N, 4, 5, N, 6};
matrix[6] = new int[]{2, 3, N, N, 4, 6, N};
// 创建Graph对象
Graph graph = new Graph(vertex, matrix);
graph.showGraph();
// 测试迪杰斯特拉算法
graph.dsj(6);
}
}

// 访问顶点集合
class VisitedVertex {
//顶点的访问情况(0代表未访问,1代表已访问),会动态更新;
public int[] already_arr;
// 记录出发顶点到其他顶点的距离,会动态更新,求出最短距离又会存放到dis中。
public int[] dis;
// 每一个下标对应的值为前一个顶点下标,会动态更新。
public int[] pre_visited;

// 构造器

/**
* @param len 顶点个数
* @param index 出发顶点的下标
*/
public VisitedVertex(int len, int index) {
this.already_arr = new int[len];
this.pre_visited = new int[len];
this.dis = new int[len];
// 初始化dis
Arrays.fill(dis, 65535);
already_arr[index] = 1; // 设置出发顶点被访问
this.dis[index] = 0; // 设置出发顶的访问距离为0
}

/**
* function:判断index顶点是否被访问过
*
* @param index
* @return 如果访问过返回true,否则返回false
*/
public boolean in(int index) {
return already_arr[index] == 1;
}

/**
* function:更新出发顶点到index顶点的距离
*
* @param index
* @param len
*/
public void updateDis(int index, int len) {
dis[index] = len;
}

/**
* function:更新pre顶点的前驱顶点为index的顶点
*
* @param pre
* @param index
*/
public void updatePre(int pre, int index) {
pre_visited[pre] = index;
}

/**
* function:返回出发顶点到index顶点的距离
*
* @param index
* @return
*/
public int getDis(int index) {
return dis[index];
}
}

// 构建图
class Graph {
private char[] vertex;// 存放顶点
private int[][] matrix; //邻接矩阵
private VisitedVertex vV; // 已经访问的顶点集合

// 构造器
public Graph(char[] vertex, int[][] matrix) {
this.vertex = vertex;
this.matrix = matrix;
}

// 显示图
public void showGraph() {
for (int[] link : matrix) {
System.out.println(Arrays.toString(link));
}
}

// 迪杰斯特拉算法

/**
* @param index 出发顶点对应的下标
*/
public void dsj(int index) {
vV = new VisitedVertex(vertex.length, index);
update(index); // 更新index顶点到周围顶点的距离和前驱顶点
}

// 更新index下标顶点到周围顶点的距离,和周围顶点的前驱顶点
private void update(int index) {
int len = 0;
// 遍历邻接矩阵matrix[index]行
for (int j = 0; j < matrix[index].length; j++) {
// len :出发顶点到index顶点的距离 + 从index顶点到j顶点的距离
len = vV.getDis(index) + matrix[index][j];
// 如果j顶点没有被访问过,并且len<出发顶点到j顶点的距离,就需要更新
if (!vV.in(j) && len < vV.getDis(j)) {
vV.updatePre(j, index);// 更新j顶点的前驱顶点为index顶点
vV.updateDis(j, len); // 更新出发顶点到j顶点的距离
}
}
}
}

运行结果

image-20221130124750333

Dijkstra算法解决最短路径问题-完整实现

代码

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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package com.algorithm.dijkstra;

import java.util.Arrays;

/**
* @author Joker大雄
* @data 2022/11/30 - 11:51
**/
public class DijkstraAlgorithm {
public static void main(String[] args) {
char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
// 邻接矩阵
int[][] matrix = new int[vertex.length][vertex.length];
final int N = 65535; // 表示不可连通
matrix[0] = new int[]{N, 5, 7, N, N, N, 2};
matrix[1] = new int[]{5, N, N, 9, N, N, 3};
matrix[2] = new int[]{7, N, N, N, 8, N, N};
matrix[3] = new int[]{N, 9, N, N, N, 4, N};
matrix[4] = new int[]{N, N, 8, N, N, 5, 4};
matrix[5] = new int[]{N, N, N, 4, 5, N, 6};
matrix[6] = new int[]{2, 3, N, N, 4, 6, N};
// 创建Graph对象
Graph graph = new Graph(vertex, matrix);
// graph.showGraph();
// 测试迪杰斯特拉算法
graph.dsj(6);
// 遍历最后结果
graph.showDsj();
}
}

// 访问顶点集合
class VisitedVertex {
//顶点的访问情况(0代表未访问,1代表已访问),会动态更新;
public int[] already_arr;
// 记录出发顶点到其他顶点的距离,会动态更新,求出最短距离又会存放到dis中。
public int[] dis;
// 每一个下标对应的值为前一个顶点下标,会动态更新。
public int[] pre_visited;

// 构造器

/**
* @param len 顶点个数
* @param index 出发顶点的下标
*/
public VisitedVertex(int len, int index) {
this.already_arr = new int[len];
this.pre_visited = new int[len];
this.dis = new int[len];
// 初始化dis
Arrays.fill(dis, 65535);
already_arr[index] = 1; // 设置出发顶点被访问
this.dis[index] = 0; // 设置出发顶的访问距离为0
}

/**
* function:判断index顶点是否被访问过
*
* @param index
* @return 如果访问过返回true,否则返回false
*/
public boolean in(int index) {
return already_arr[index] == 1;
}

/**
* function:更新出发顶点到index顶点的距离
*
* @param index
* @param len
*/
public void updateDis(int index, int len) {
dis[index] = len;
}

/**
* function:更新pre顶点的前驱顶点为index的顶点
*
* @param pre
* @param index
*/
public void updatePre(int pre, int index) {
pre_visited[pre] = index;
}

/**
* function:返回出发顶点到index顶点的距离
*
* @param index
* @return
*/
public int getDis(int index) {
return dis[index];
}

// 继续选择并返回新的访问顶点,例如G顶点访问过后,就以A作为新的访问顶点
public int updateArr() {
int min = 65535, index = 0;
for (int i = 0; i < already_arr.length; i++) {
if (already_arr[i] == 0 && dis[i] < min) {
min = dis[i];
index = i;
}
}
// 更新index顶点被访问过了
already_arr[index] = 1;
return index;
}

// 显示最后结果
public void show() {
System.out.println("----------------------");
// 输出already_arr
for (int i : already_arr) {
System.out.print(i + " ");
}
System.out.println();
// 输出pre_visited
for (int i : pre_visited) {
System.out.print(i + " ");
}
System.out.println();
// 输出dis
for (int i : dis) {
System.out.print(i + " ");
}
System.out.println();
System.out.println("----------------------");
// 优化输出效果
char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
int count = 0;
for (int i : dis) {
if (i != 65535) {
System.out.print("G-"+vertex[count] + "(" + i + ") ");
} else {
System.out.println("N");
}
count++;
}
System.out.println();
}
}

// 构建图
class Graph {
private char[] vertex;// 存放顶点
private int[][] matrix; //邻接矩阵
private VisitedVertex vV; // 已经访问的顶点集合

// 构造器
public Graph(char[] vertex, int[][] matrix) {
this.vertex = vertex;
this.matrix = matrix;
}

// 显示图
public void showGraph() {
for (int[] link : matrix) {
System.out.println(Arrays.toString(link));
}
}

// 迪杰斯特拉算法

/**
* @param index 出发顶点对应的下标
*/
public void dsj(int index) {
vV = new VisitedVertex(vertex.length, index);
update(index); // 更新index顶点到周围顶点的距离和前驱顶点

for (int i = 1; i < vertex.length; i++) {
index = vV.updateArr(); // 选择并返回新的访问顶点
update(index);// 更新index顶点到周围顶点的距离和前驱顶点
}
}

// 更新index下标顶点到周围顶点的距离,和周围顶点的前驱顶点
private void update(int index) {
int len = 0;
// 遍历邻接矩阵matrix[index]行
for (int j = 0; j < matrix[index].length; j++) {
// len :出发顶点到index顶点的距离 + 从index顶点到j顶点的距离
len = vV.getDis(index) + matrix[index][j];
// 如果j顶点没有被访问过,并且len<出发顶点到j顶点的距离,就需要更新
if (!vV.in(j) && len < vV.getDis(j)) {
vV.updatePre(j, index);// 更新j顶点的前驱顶点为index顶点
vV.updateDis(j, len); // 更新出发顶点到j顶点的距离
}
}
}

// 显示最后结果
public void showDsj() {
vV.show();
}
}

运行结果

1
2
3
4
5
6
7
8
----------------------
1 1 1 1 1 1 1
6 6 0 5 6 6 0
2 3 9 10 4 6 0
----------------------
G-A(2) G-B(3) G-C(9) G-D(10) G-E(4) G-F(6) G-G(0)

Process finished with exit code 0