定义
迪杰斯特拉算法用于计算图中,某一个顶点vi到另外一个顶点vj的最短路径,因为计算从vi到vj的最短路径实际需要计算出vi到vj之前的所有顶点的最短路径,所以实际的计算可能会附带得出vi到图中一部分节点的最短路径,而且这种“附带”的结果是必需的。
算法过程如下:
设置出发顶点为v,顶点集合V{v1,v2,vi...},v到V中各顶点的距离构成距离集合Dis,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、重复执行两步骤,直到最短路径顶点为目标顶点即可结束。
示例
给{A,B,C,D,E,F,G}对应序号为{0,1,2,3,4,5,6},演示从G顶点,即序号6到图中各顶点的最短路径。
=======================================
最短路径:G
顶点前驱:G->A,G->B,G->E,G->F
到各顶点距离:A:2,B:3,E:4,F:6
=======================================
最短路径:A
顶点前驱:G->A,G->B,G->E,G->F
到各顶点距离:A:2,B:3,C:9,E:4,F:6
=======================================
最短路径:A,B
顶点前驱:G->A,G->B,B->D,G->E,G->F
到各顶点距离:A:2,B:3,C:9,D:12,E:4,F:6
=======================================
最短路径:A,B,E
顶点前驱:G->A,G->B,B->D,G->E,G->F
到各顶点距离:A:2,B:3,C:9,D:12,E:4,F:6
=======================================
最短路径:A,B,E,F
顶点前驱:G->A,G->B,F->D,G->E,G->F
到各顶点距离:A:2,B:3,C:9,D:10,E:4,F:6
=======================================
最短路径:A,B,C,E,F
顶点前驱:G->A,G->B,A->C,F->D,G->E,G->F
到各顶点距离:A:2,B:3,C:9,D:10,E:4,F:6
=======================================
最短路径:A,B,C,D,E,F
顶点前驱:G->A,G->B,A->C,F->D,G->E,G->F
到各顶点距离:A:2,B:3,C:9,D:10,E:4,F:6
参考代码
class t{
public static void main(String[] args){
char[] vertex=new char[]{'A','B','C','D','E','F','G'}; //顶点数组
int[][] matrix=new int[7][]; //邻接矩阵
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 G=new Graph(vertex,matrix); //构建图对象
//上述为测试用例
G.dsj(6);
}
}
class Graph{
private char[] vertex; //顶点数组
private int[][] matrix; //邻接矩阵
private Visited_vertex vi; //已访问的顶点集合
public Graph(char[] vertex,int[][] matrix){
this.vertex=vertex;
this.matrix=matrix;
}
public void dsj(int index){ //迪杰斯特拉算法计算最短路径
vi=new Visited_vertex(vertex.length,index);//初始化访问顶点集合对象
update(index);//更新index下标顶点到周围顶点的距离与周围顶点的前驱顶点
vi.show();//查看中间过程
for(int j=1;j<vertex.length;j++){
index=vi.updateArr();//选择并返回新的访问顶点
update(index);//更新从出发顶点到index周围各顶点的距离
vi.show();
}
}
private void update(int index){//更新从出发顶点到index周围各顶点的距离
int len=0;
for(int j=0;j<matrix[index].length;j++){
/*下面这个判断条件是指:j顶点还没有被访问,并且从出发顶点到index顶点的距离,加上
从index顶点到j顶点的距离,小于从出发顶点到j顶点的距离 */
len=vi.getDis(index)+matrix[index][j];
if(!vi.in(j)&&len<vi.getDis(j)){
vi.updatePre(j,index);//更新j顶点的前驱为index节点
vi.updateDis(j,len);//更新出发节点到j顶点的距离
}
}
}
}
class Visited_vertex{ //已访问顶点集合
public int[] already_arr;//已访问则对应下标上赋值为1,默认为0
public int[] pre_visited; //每个下标对应的存储值为前一个顶点下标
public int[] dis; //记录出发顶点到其他所有顶点的距离
public Visited_vertex(int length,int index){
this.already_arr=new int[length];
this.pre_visited=new int[length];
this.dis=new int[length];
Arrays.fill(dis,65535);//设置初始距离
already_arr[index]=1;//设置出发顶点为已访问
dis[index]=0;//设置出发顶点访问距离为0
}
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;
}
}
already_arr[index]=1;
return index;
}
public boolean in(int index){//判断index顶点是否被访问过
return already_arr[index]==1;
}
public void updateDis(int index,int len){//更新出发节点到j顶点的距离
dis[index]=len;
}
public void updatePre(int pre,int index){//更新j顶点的前驱为index节点
pre_visited[pre]=index;
}
public int getDis(int index){//返回从出发顶点到index顶点的距离
return dis[index];
}
public void show(){
System.out.println("=============================");
for(int i:already_arr){
System.out.print(i+" ");
}
System.out.println();
for(int i:pre_visited){
System.out.print(i+" ");
}
System.out.println();
for(int i:dis){
if(i!=65535){
System.out.print(i+" ");
}else{
System.out.print("N ");
}
}
System.out.println();
}
}
测试结果
E:\>java t
=============================
0 0 0 0 0 0 1 序号为1表示已访问过,标记作为已到达最短路径
6 6 0 0 6 6 0 表示对应下标的顶点的前驱顶点,如6表示G,A(0),B(1),E(4),F(5)前驱都是G(6)
2 3 N N 4 6 0 表示出发顶点G到各下标的顶点的距离,A(0)为2,B(1)为3
=============================
1 0 0 0 0 0 1
6 6 0 0 6 6 0
2 3 9 N 4 6 0
=============================
1 1 0 0 0 0 1
6 6 0 1 6 6 0
2 3 9 12 4 6 0
=============================
1 1 0 0 1 0 1
6 6 0 1 6 6 0
2 3 9 12 4 6 0
=============================
1 1 0 0 1 1 1
6 6 0 5 6 6 0
2 3 9 10 4 6 0
=============================
1 1 1 0 1 1 1
6 6 0 5 6 6 0
2 3 9 10 4 6 0
=============================
1 1 1 1 1 1 1
6 6 0 5 6 6 0
2 3 9 10 4 6 0
总结
迪杰斯特拉算法计算最短路径,可以近似看做从出发顶点向外“推进”的过程,也就是距离值一直在变大,直到推动目标顶点。以一个记录前驱顶点下标的数组来表示路径,例如从G顶点,即序号下标为6的顶点出发,路径数组值为{6,6,0,5,6,6,0},则计算G到D的最短路径,D(序号:3)的前驱为F(序号:5),F前驱为G(序号:6),可知由G到D的最短路径为:G->F->D。