acm-最短路径算法

最短路径算法

一、求出最短路径的长度

以下没有特别说明的话,dis[u][v]表示从u到v最短路径长度,w[u][v]表示连接u,v的边的长度。1.Floyed-Warshall算法 O(N3)   

简称Floyed(弗洛伊德)算法,是最简单的最短路径算法,可以计算图中任意两点间的最短路径。Floyed的时间复杂度是O (N3),适用于出现负边权的情况。

算法描述:

初始化:点u、v如果有边相连,则dis[u][v]=w[u][v]。  

如果不相连则dis[u][v]=0x7fffffff

For (k = 1; k <= n; k++)    

For (i = 1; i <= n; i++) 

For (j = 1; j <= n; j++)    

If (dis[i][j] >dis[i][k] + dis[k][j])        

dis[i][j] = dis[i][k] + dis[k][j]; 

算法结束:dis[i][j]得出的就是从i到j的最短路径。

 

Floyed算法变形:  

如果是一个没有边权的图,把相连的两点间的距离设为dis[i][j]=true,不相连的两点设为dis[i][j]=false,用Floyed算法的变形:

For (k = 1; k <= n; k++)  

For (i = 1; i <= n; i++)    

For (j = 1; j <= n; j++)    

dis[i][j] = dis[i][j] || (dis[i][k] && dis[k][j]);       

用这个办法可以判断一张图中的两点是否相连。 

2.Dijkstra算法O (N2)

用来计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法。也就是说,只能计算起点只有一个的情况。

Dijkstra的时间复杂度是O (N2),它不能处理存在负边权的情况。

算法描述:       

设起点为s,dis[v]表示从s到v的最短路径,pre[v]为v的前驱节点,用来输出路径。       

a)初始化:dis[v]=∞(v≠s); dis[s]=0; pre[s]=0;        b)For (i = 1; i <= n ; i++)            

1.在没有被访问过的点中找一个顶点u使得dis[u]是最小的。            

2.u标记为已确定最短路径            

3.For 与u相连的每个未确定最短路径的顶点v              if  (dis[u]+w[u][v] < dis[v])                

{                  

dis[v] = dis[u] + w[u][v];                  

pre[v] = u;               

}        

c)算法结束:dis[v]为s到v的最短距离;pre[v]为v的前驱节点,用来输出路径。

 

3.Bellman-Ford算法O(NE)

简称Ford(福特)算法,同样是用来计算从一个点到其他所有点的最短路径的算法,也是一种单源最短路径算法。能够处理存在负边权的情况,但无法处理存在负权回路的情况。

算法时间复杂度:O(NE),N是顶点数,E是边数。

算法实现:设s为起点,dis[v]即为s到v的最短距离,pre[v]为v前驱。w[j]是边j的长度,且j连接u、v。

初始化:dis[s]=0,dis[v]=∞(v≠s),pre[s]=0

For (i = 1; i <= n-1; i++)

For (j = 1; j <= E; j++)         //注意要枚举所有边,不能枚举点。   

if (dis[u]+w[j]

{       dis[v] =dis[u] + w[j];       pre[v] = u;  }

 

Bellman-Ford算法无法解决负权回路的问题

 

4、SPFA算法O(kE)SPFA是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。

算法时间复杂度:O(kE),E是边数。K是常数,平均值为2。

算法实现:    

dis[i]记录从起点s到i的最短路径,w[i][j]记录连接i,j的边的长度。pre[v]记录前趋。    

team[1..n]为队列,头指针head,尾指针tail。    布尔数组exist[1..n]记录一个点是否现在存在在队列中。    

初始化:

dis[s]=0,dis[v]=∞(v≠s),memset(exist,false,sizeof(exist));    

起点入队team[1]=s; head=0; tail=1;exist[s]=true;    do{     

1、头指针向下移一位,取出指向的点u。    

2、exist[u]=false;已被取出了队列    

3、for与u相连的所有点v          //注意不要去枚举所有点,用数组模拟邻接表存储

if (dis[v]>dis[u]+w[u][v])      

{          

dis[v]=dis[u]+w[u][v];          

pre[v]=u;          

if (!exist[v]) //队列中不存在v点,v入队。          

{//尾指针下移一位,v入队;               

exist[v]=true;           

}      

}    

}    

while (head < tail);

 

循环队列:  采用循环队列能够降低队列大小,队列长度只需开到2*n+5即可。例题中的参考程序使用了循环队列。

你可能感兴趣的:(acm-最短路径算法)