1,应用场景—最短寻径问题
- 如图,存在7个村庄
['A', 'B', C', 'D', 'E', 'F', 'G']
,现在有6个邮差,从G点出发,需要分别赶往['A', 'B', C', 'D', 'E', 'F']
六个村庄
- 各个村庄的距离通过边的权值表示,如
A <-> B = 5
- 问:如何计算G村庄到其他村庄的最短距离
- 注意:之前两篇说的普里姆算法和克鲁斯卡尔算法,都是求图内连接各个节点的最短路径;该问题是从一点出发,到各个顶点的最短路径。注意,此处是到各个顶点,不是总和的最短路径
2,迪杰斯塔拉算法介绍
- 首先构建出图的顶点集合和邻接图表,并确定出发顶点
- 然后根据图表信息和出发顶点信息构建顶点访问情况对象,其中包括三个数组属性:
- 顶点访问情况数组:0表示未访问, 1表示已访问
- 顶点距离数组:各个顶点到出发顶点的最短距离汇总,也是最终的结果;出发顶点到出发顶点的距离为0,其他顶点到出发顶点的距离初始化为极值
- 前驱顶点数组:即访问当前顶点的上一个顶点,经过几次前继查找,必定会查找到出发顶点,该数组可能计算完成后,用户复原路线图
- 构建完成后,开始进行计算
- 先处理出发顶点,处理之前,先标记当前顶点为已访问,然后从邻接图标中获取该顶点的连接数组,用户顶点距离数组填充,已连接的填充真实距离,未连接的填充为极值(此处注意,出发节点与部分节点未连通,后续处理中会广度遍历其他节点,判断连通);之后将所有连接节点的前驱节点修改为出发节点
- 出发顶点处理完成后,对所有与出发顶点关联的节点已经初步统计完成;但是,此处还存在间接关联的节点没有统计;另外,直接连接的距离不一定比间接连接的距离近,比如A-B=5,B-C=10,A-C=20,这样从A-B-C=15,间接距离是小于直接距离的(此处可不用考虑合理性,只做场景分析,不排除翻山越岭)
- 出发顶点处理完成后,按照图广度遍历优先的原则,从距离数组中,依次取出距出发顶点距离最小的未访问顶点,进行访问顶点处理
- 访问顶点在遍历顶点集合判断距离时,用访问顶点到出发顶点的距离与访问顶点到当前遍历顶点的距离之和(出发顶点通过间接方式到当前遍历顶点的距离),与当前遍历顶点到出发顶点的距离(直接方式到当前遍历顶点的距离,不存在直接方式则为极值)取最小值,进行顶点距离和顶点前驱节点的覆盖
- 循环第3,第4步,直到所有顶点全部为已访问,则计算完成
- 计算全部完成后,顶点访问情况对象里面的顶点距离数组,即为最终的结果呈现!
3,代码实现
package com.self.datastructure.algorithm.dijkstra;
import lombok.Getter;
import java.util.Arrays;
public class Dijkstra {
private static final int NON = Integer.MAX_VALUE;
public static void main(String[] args) {
char[] lstVertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
int[][] vertexMap = {
{NON, 5, 7, NON, NON, NON, 2},
{5, NON, NON, 9, NON, NON, 3},
{7, NON, NON, NON, 8, NON, NON},
{NON, 9, NON, NON, NON, 4, NON},
{NON, NON, 8, NON, NON, 5, 4},
{NON, NON, NON, 4, 5, NON, 6},
{2, 3, NON, NON, 4, 6, NON}
};
MyGraph myGraph = new MyGraph(lstVertex, vertexMap);
dijkstra(myGraph);
}
private static void dijkstra(MyGraph myGraph) {
int index = myGraph.getVertexCount() - 1;
VisitedVertex visitedVertex = new VisitedVertex(myGraph.getVertexCount(), index);
updateVisitedVertex(visitedVertex, myGraph, index);
for (int i = 0; i < myGraph.getVertexCount() - 1; i++) {
index = getNextIndex(visitedVertex);
updateVisitedVertex(visitedVertex, myGraph, index);
}
System.out.println("最终结果: " + Arrays.toString(visitedVertex.getVertexDis()));
}
private static int getNextIndex(VisitedVertex visitedVertex) {
int[] visitedArr = visitedVertex.getVisitedArr();
int minDis = NON;
int index = -1;
for (int i = 0; i < visitedArr.length; i++) {
if (visitedArr[i] == 0 && visitedVertex.getVertexDis()[i] < minDis) {
index = i;
minDis = visitedVertex.getVertexDis()[i];
}
}
return index;
}
private static void updateVisitedVertex(VisitedVertex visitedVertex, MyGraph myGraph, int index) {
visitedVertex.getVisitedArr()[index] = 1;
int[][] vertexMap = myGraph.getVertexMap();
int[] distanceArr = vertexMap[index];
for (int i = 0; i < distanceArr.length; i++) {
if (distanceArr[i] != NON) {
if (visitedVertex.getVisitedArr()[i] == 0
&& (myGraph.getVertexMap()[index][i] + visitedVertex.getVertexDis()[index] < visitedVertex.getVertexDis()[i])) {
visitedVertex.getVertexDis()[i] = (myGraph.getVertexMap()[index][i] + visitedVertex.getVertexDis()[index]);
visitedVertex.getPreVertexArr()[i] = index;
}
}
}
}
@Getter
static class VisitedVertex {
private int[] visitedArr;
private int[] vertexDis;
private int[] preVertexArr;
public VisitedVertex(int vertexCount, int startIndex) {
this.visitedArr = new int[vertexCount];
this.vertexDis = new int[vertexCount];
Arrays.fill(vertexDis, NON);
this.vertexDis[startIndex] = 0;
this.preVertexArr = new int[vertexCount];
}
}
@Getter
static class MyGraph {
private int vertexCount;
private char[] lstVertex;
private int[][] vertexMap;
public MyGraph(char[] lstVertex, int[][] vertexMap) {
this.vertexCount = lstVertex.length;
this.lstVertex = lstVertex;
this.vertexMap = vertexMap;
}
}
}