Dijkstra算法(Java实现)
核心思想
从一开始先确定一个最短路径,然后不断去更新该路径,最终得到起始节点到其余节点的最短路径。
代码
public class Dijkstra {
private static final int MAX_DISTANCE = 1000; // 无限距离,用于初始化距离
public static void main(String[] args) {
int max = MAX_DISTANCE;
int[][] graph = {
{0, 10, max, 4, max, max},
{10, 0, 8, 2, 6, max},
{max, 8, 10, 15, 1, 5},
{4, 2, 15, 0, 6, max},
{max, 6, 1, 6, 0, 12},
{max, max, 5, max, 12, 0}};
System.out.println("输入起始节点:");
@SuppressWarnings("resource")
Scanner in = new Scanner(System.in);
int startNode = Integer.parseInt(in.nextLine());
int[] dist = dijkstra(graph, startNode);
System.out.println(Arrays.toString(dist));
}
/**
* Dijkstra算法,其调用了findMinDistNode方法
* @param graph 进行查找的图,这里用二维矩阵实现
* @param startNode 起始节点
* @return 返回起始节点到其他节点的最短路径距离列表
*/
public static int[] dijkstra(int[][] graph, int startNode) {
char[] vertexList = {'A', 'B', 'C', 'D', 'E', 'F'};
String[] findProcess = new String[6]; // 用于打印最短路径的字符串数组
findProcess[0] = String.valueOf(vertexList[startNode]); // 直接将开始节点赋给findProcess[0]
int[] processDist = new int[6]; // 用于打印最短路径距离的整型数组(和findProcess搭配打印)
processDist[0] = 0;
int vertexNumbers = graph.length;
int[] dist = new int[vertexNumbers]; // 当下startNode到每个顶点的最短距离
Arrays.fill(dist, MAX_DISTANCE); // 将初始距离都设为最大值
dist[startNode] = 0; // startNode和自己的距离为0
boolean[] visited = new boolean[vertexNumbers]; // 该列表标记顶点是否被放入最终最短路径中
// 第一个for循环用于找到minNode,将其加入最终的最短路径
for (int i = 0; i < vertexNumbers; i++) {
int minNode = findMinDistNode(dist, visited); // 未被访问且startNode能达到的最短距离的顶点
visited[minNode] = true; // 将此顶点加入已访问列表,保证之后循环findMinDistNode方法时不再访问该顶点,因为已经确定将该点加入最终最短路径了
if (i == 0) ; // 开始节点到开始节点不进行任何操作
else {
findProcess[i] = findProcess[i-1] + " -> " + vertexList[minNode]; // 开始节点到每次确定下的minNode之间的路径
}
processDist[i] = dist[minNode]; // 开始节点到每次确定下的minNode之间的距离
// 第二个for循环以minNode顶点为新的开始点进行后续节点的查找,保证从startNode到后续节点的距离为当下最小距离
for (int j = 0; j < vertexNumbers; j++) {
/*
* minNode和后续节点j满足几个条件:
* 1、后续节点j未被访问过
* 2、在图中minNode和后续节点距离不为0,即不是同一个节点
* 3、minNode和startNode距离不为最大值
* 4、startNode到minNode的最短距离(dist[minNode])加minNode和后续节点的距离小于startNode直接到后续节点的最短距离(dist[j])
*/
if (!visited[j] && graph[minNode][j] != 0 && dist[minNode] != MAX_DISTANCE
&& dist[minNode] + graph[minNode][j] < dist[j]) {
dist[j] = dist[minNode] + graph[minNode][j]; // 更新dist列表,保证dist[j]是目前为止的最小值
}
}
}
System.out.println("起始节点到各节点的最短路径及距离如下:");
for (int i = 1; i < 6; i++) {
System.out.println(findProcess[i] + " 的最短距离为 " + processDist[i]);
}
return dist;
}
// 从dist[]中其余未访问的节点中找到startNode能到达的距离最小的顶点
public static int findMinDistNode(int[] dist, boolean[] visited) {
int minDist = MAX_DISTANCE;
int minNode = -1;
for (int i = 0; i < dist.length; i++) {
// 若距离为MAX_DISTANCE,则说明startNode无法到达该顶点
if (!visited[i] && dist[i] < minDist) {
minDist = dist[i]; // 更新minDist,保证最后得出的是可达到的距离最短的顶点
minNode = i; // 更新minNode
}
}
return minNode;
}
}
重要变量说明
变量 | 说明 |
---|---|
startNode | 起始节点,一旦被赋值就不会变 |
graph | 进行查找的图,用二维矩阵实现 |
dist[] | 保存起始节点到其他节点的最短距离,该数组元素会不断更新,这里的起始节点是控制台输入的最初的起始节点,之后不会改变 |
vertexList[] | 顶点数组,对应dist[] |
findProcess[] | 用于打印最短路径的字符串数组 |
processList[] | 用于打印最短路径距离的整型数组(和findProcess搭配打印) |
visited[] | 该列表标记顶点是否被放入最终最短路径中 |
minNode | 当下startNode能达到的最短距离的顶点,会加入到最终dist[]中 |
手动推导Dijkstra算法过程(以初始节点为A点为例)
1、确定startNode为vertexList[0] 即A点;
2、找到A能到达的点,并将距离记录在dist[] 中;
3、选择除了A点之外dist[] 中最小距离所对应的点作为minNode,此表中为D点;
4、计算A通过D到其他各点的距离,更新dist[] ,确保各元素值为当下的最小值,此时D点已加入最短路径,标记为已访问;
5、重复上述3、4操作,得到最终的dist[] 数组,即A点到各点的最短路径距离。
具体过程如下图:
左侧黑色的表代表dist[],红色圈出来的代表已加入最终最短路径的节点minNode,右侧红色的过程代表以minNode为中介访问其他节点的过程
代码执行结果如下:
输入起始节点:
0
起始节点到各节点的最短路径及距离如下:
A -> D 的最短距离为 4
A -> D -> B 的最短距离为 6
A -> D -> B -> E 的最短距离为 10
A -> D -> B -> E -> C 的最短距离为 11
A -> D -> B -> E -> C -> F 的最短距离为 16
[0, 6, 11, 4, 10, 16]
参考文章:
[1] https://www.cnblogs.com/goldsunshine/p/12978305.html
[2] https://developer.aliyun.com/article/1181921