应用场景
概述
- Dijkstra算法 是典型单源最短路径算法,用于计算一个顶点到其他顶点的最短路径
- 单源:从一个顶点出发,Dijkstra算法 只能求一个顶点到其他点的最短距离而不能任意两顶点
- 用于无权图,或者所有边的权都相等的图,Dijkstra 算法等同于BFS搜索
- 特点:以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止
- Dijkstra算法 用于对有权图进行搜索,找出图中两点的最短距离,既不是DFS搜索,也不是BFS搜索
基本思想
- 设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组
- 第 1 组为已求出最短路径的顶点集合 (用S表示),初始时S中只有一个起点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了
- 第 2 组为其余未确定最短路径的顶点集合 (用U表示),按最短路径长度的递增次序依次把第 2 组的顶点加入S中,在加入的过程中,总保持从起点s到S中各顶点的最短路径长度不大于从起点s到U中任何顶点的最短路径长度
- 此外,每个顶点对应一个距离
- S中的顶点的距离就是从v到此顶点的最短路径长度
- U中的顶点的距离,是从v到此顶点 只包括S中的顶点为中间顶点 的当前最短路径长度
- 初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是"起点s到该顶点的路径"。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径
- 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。重复该操作,直到遍历完所有顶点
操作演示
https://blog.csdn.net/kprogram/article/details/81225176
https://zhuanlan.zhihu.com/p/40338107
- 实际上,Dijkstra 算法是一个排序过程,就上面的例子来说,是根据D到图中其余点的最短路径长度进行排序,路径越短越先被找到,路径越长越靠后才能被找到
- 可见,要找D到A的最短路径,我们依次找到了:
- D → C 的最短路径 3
- D → E 的最短路径 4
- D → E → F 的最短路径 6
- D → E → G 的最短路径 12
- D → C → B 的最短路径 13
- D → E → F → A 的最短路径 22
代码实现
public class DijkstraAlgorithm {
public static void main(String[] args) {
char[] vertexs = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
int[][] matrix = new int[vertexs.length][vertexs.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 = new Graph(vertexs, matrix);
graph.dijkstra(2); // C
graph.showShortestPath();
}
}
class VisitedVertex {
int[] alreadyArr; // 记录已访问顶点
int[] preVisited; // 各个顶点的前驱顶点
int[] dis; // 起点到各个顶点的距离
public VisitedVertex(int length, int index) {
alreadyArr = new int[length];
alreadyArr[index] = 1;
preVisited = new int[length];
dis = new int[length];
// 初始化 dis[]
for(int i = 0; i < length; i++)
dis[i] = 65535;
dis[index] = 0; // 出发顶点的访问距离为0
}
/**
* 判断 顶点index 是否被访问过
* @param index
* @return 如果访问过返回true; 反之false
*/
public boolean isVisited(int index) {
return alreadyArr[index] == 1;
}
/**
* 更新 出发顶点 到 顶点index 的距离为len
* @param index
* @param len
*/
public void updateDis(int index, int len) {
dis[index] = len;
}
/**
* 更新 顶点index 的前驱 为 preV
* @param index 顶点
* @param preV 顶点的前驱
*/
public void updatePreVertex(int index, int preV) {
preVisited[index] = preV;
}
/**
* 返回 出发顶点 到 顶点index 的距离
* @param index
*/
public int getDis(int index) {
return dis[index];
}
// 选择新的访问顶点
public int getVisitVertex() {
int min = 65535, index = 0;
for(int i = 0; i < alreadyArr.length; i++)
if(alreadyArr[i] == 0 && dis[i] < min) {
min = dis[i];
index = i;
}
// 设置 顶点index 为 已访问
alreadyArr[index] = 1;
return index;
}
}
class Graph {
char[] vertexs;
int[][] matrix;
VisitedVertex vv;
public Graph(char[] vertexs, int[][] matrix) {
super();
this.vertexs = vertexs;
this.matrix = matrix;
}
/**
* 求单源最短路径
* @param index 出发顶点的索引
*/
public void dijkstra(int index) {
vv = new VisitedVertex(vertexs.length, index);
update(index); // {出发顶点}
for(int j = 1; j < vertexs.length; j++) {
index = vv.getVisitVertex();
update(index); // {访问顶点}
}
}
// 更新 顶点index 到周围顶点的距离 及 周围顶点的前驱顶点
private void update(int index) {
int len = 0;
// 遍历: 顶点index 连接关系的那一行
for(int j = 0; j < matrix[index].length; j++) {
// 出发顶点 经 顶点index 到 顶点j 的距离
len = vv.getDis(index) + matrix[index][j];
if(!vv.isVisited(j) && len < vv.getDis(j)) {
vv.updatePreVertex(j, index);
vv.updateDis(j, len);
}
}
}
public void showShortestPath() {
for(int i = 0; i < vv.dis.length; i++)
System.out.printf("%c(%d) ", vertexs[i], vv.dis[i]);
}
}