该算法按从源点到其它个点的最短路长度递增的顺序,依次确定源点到每个点的最短路。要求图中不能有负权的边。
Java实现的代码如下:
import java.util.*;
public class Main {
/*
* 默认源点为0
*/
static Scanner sc=new Scanner(System.in);
static int n,m;//顶点的个数,边的个数
static long[][] g;//图的邻接矩阵
static int[] prior;//前驱
static long[] d;//最短路的距离
static boolean[] s;//已确定的点的集合
static int INF=Integer.MAX_VALUE;
static void gInitialize(){//有向图的初始化
n=sc.nextInt();
m=sc.nextInt();
g=new long[n][n];
for(int i=0;i<g.length;i++)
for(int j=0;j<g[i].length;j++)
g[i][j]=INF;
for(int i=0;i<m;i++)
g[sc.nextInt()][sc.nextInt()]=sc.nextInt();
}
static void dijkstra(){
prior=new int[n];
d=new long[n];
s=new boolean[n];
for(int i=0;i<n;i++){
d[i]=g[0][i];
}
s[0]=true;
d[0]=0;
while(true){//此循环体仅用来限制计算次数,每次确定一个顶点
int v=-1;//中间点
for(int i=0;i<n;i++){//从尚未使用过的顶点中选择一个距离最小的顶点,作为中间点
if(!s[i]&&(v==-1||d[i]<d[v])){
v=i;
}
}
if(v==-1)//已经没有可以确定的顶点,算法结束
break;
s[v]=true;
for(int i=0;i<n;i++){//用该中间点更新d
if(!s[i]&&(d[v]+g[v][i]<d[i])){
d[i]=d[v]+g[v][i];//更新
prior[i]=v;//更改前驱为v
}
}
}
}
public static void main(String[] args){
gInitialize();
dijkstra();
System.out.println(d[sc.nextInt()]);//输出源点到一个指定终点的最短路距离
}
}
1、算法正确性的简单证明:
在最开始时,只有起点的最短距离是确定的。而在尚未使用过的顶点中,距离d[i]最小的顶点就是最短距离已经确定的顶点。这是因为由于不存在负边,所以d[i]不会在之后的更新中变小。
2、时间复杂度:
邻接矩阵 O(n^2)
邻接表 O(n^2)
邻接表+binary heap O((n+m)logn)
邻接表+fibonacci heap O(m+nlogn) priority_queue O(mlogn)
3、该算法得到的最短路径生成树是最小生成树吗?
不一定,很少情况下是。这是由两个算法最后的循环体内的松弛操作的不同决定的(更新d或mincost)。简单来说,Dijkstra的松弛操作加上了到源点的距离,而Prim只考虑相邻节点的权值。
当然也可以通过反例来证明:
设1为源点,则最短路径生成树的权值和为7,最小生成树的权值和为6,显然不同。