Dijkstra算法使用的场合
1、单源:求一个源节点s到其他某些节点的最短路径
2、权重非负:节点之间的权重不能小于0
3、有向:结点之间的权重都是有向的
Dijkstra的描述
最短路径估计g(v):当前求得的s到v的最短距离(该路径不一定是最短路径)
最短路径d(v):s到v的最短路径距离
Dijkstra算法在运行的过程中维持的关键信息是一组点的集合S。从源节点s到该集合S中每个节点的最短路径已经找到。算法重复从节点集V-S中选择当前最短路径估计最小的节点u,将u加入到集合S,然后对所有从u出发的边进行松弛。
Dijkstra的java模板
int n;//节点的个数
int s;//源节点
int d[]=new int[n];//d[v]:s到v的最短距离
int vis[]=new int[n];//vis[v]=1:节点v属于集合S
int g[]=new int[n];//g[v]:节点的v的最短估计路径
int w[][]=new int[n][n];//w[i][j]:节点i到节点j的权重
void dijkstra()
{
int MAX=0xffffff;
int min=0;
int tmp=0;
Arrays.fill(g, MAX);
Arrays.fill(w, -1);
对w赋值
g[s]=0;
for(int i=0;i<n;i++)
{
//*//找出V-S中最短估计路径距离最小的节点tmp
min=MAX;
for(int j=0;j<n;j++)
{
if(vis[j]==0 && g[j]<min)
{
tmp=j;
min=g[j];
}
}
//*//
vis[tmp]=1;//将节点tmp加入到S中
//**//对所有从tmp出发的节点进行松弛
for(int j=0;j<n;j++)
{
if(vis[j]==0 && w[tmp][j]!=-1 && g[j]>g[tmp]+w[tmp][j])
{
g[j]=g[tmp]+w[tmp][j];
}
}
//**//
}
}
关键在于证明:对于每次选择加入到集合S中的节点u,都有g(u)=d(u)。
在证明之前先给出一条性质(摘自算法导论)
收敛性质:对于某些节点u,v,如果s~u~v是一条最短路径,并且在对边(u,v)进行松弛钱的任意时间有g[u]=d[u],则在之后的所有时间有g[v]=d[v]。
假设u是加入到S集合中的第一个不满足g[u]!=d[u]的节点,那么必然存在着一条从s到u的最短路径p,我们假设y是V-S上的一点,x是y的前驱节点,且x属于S(y可能是u,x可能是s)。
1、由于p是s到u的最短路径,所以p上路径s~y也是s到y的最短路径。
2、由于u是加入到S集合的第一个不满足g[u]=d[u]的节点,所以g[x]=d[x]。当x加入到S时,边(x,y)会被松弛
由1、2和收敛性质可知:g[y]=d[y]。
3、由于p是s到u最短路径,y是最短路径上位于u之前的节点,所以d[y]<=d[u]
根据以上两个个式子可推出:g[y]=d[y]<=d[u]<=g[u]
4、由于选着加入S时选择的是u,且u和y都在集合V-S中,所以又g[y]>=g[u]
所以,g[y]=d[y]=d[u]=g[u]。
这与假设矛盾。