bellman-ford算法能够求带负权值的单源最短路径。但是这个算法的时间复杂度(v*E)还是比较高。所以用spfa算法(SPFA无法处理带负环的图)对其优化(利用队列)。
spfa算法的原理:(引用内容)
如何求得最短路径的长度值? 首先说明,SPFA是一种单源最短路径算法,所以以下所说的“某点的最短路径长度”,指的是“起点到源点的最短路径长度”。
我们记源点为S,由源点到达点i的“当前最短路径”为D[i],开始时将所有D[i]初始化为无穷大,D[S]则初始化为0。算法所要做的,就是在运行过程中,不断尝试减小D[]数组的元素,最终将其中每一个元素减小到实际的最短路径。
过程中,我们要维护一个队列,开始时将源点置于队首,然后反复进行这样的操作,直到队列为空:
(1)从队首取出一个结点u,扫描所有由u结点可以一步到达的结点,具体的扫描过程,随存储方式的不同而不同;
(2)一旦发现有这样一个结点,记为v,满足D[v] > D[u] + w(u, v),则将D[v]的值减小,减小到和D[u] + w(u, v)相等。其中,w(u, v)为图中的边u-v的长度,由于u-v必相邻,所以这个长度一定已知(不然我们得到的也不叫一个完整的图);这种操作叫做松弛。
伪代码:
算法描述
输入设L是用来表示有向图G=(V,E)的邻接表,邻接表元素l是有向图各边的权值,输入各边的权值l(v,k)建立邻接表L.
输出设D数组是记录当前从源点到其余各点的最短路径的值,初始化时D数组的每个元素都为最大值,经过SPFA算法D数组输出各点的最短路径值。
算法的形式算法描述及注释:
1 begin //算法开始
2 for each v in V do
Begin
For each k∈L[v] do read (l(v,k)); //读入每条边的权值到邻接表
QM[v]=0; //初始化每个顶点是否在队里的标志数组
D[v]=MAX;//将最短路径数组初始化为最大值
End;
3 queue<-v0;
QM[v0]=1; //源点v0入queue队
4 D[v0]=0; //源点到源点本身的路径值赋值为零
5 while queue not empty do
Begin
W<-queue; //从queue中取出一个点w
QM[w]=0; //w点出队后,其标志数组元素改为零,表示w点不在队列
6 for each j∈L[w] do
7 if D[j]>D[w]+l(w,j) then
begin
8 D[j]=D[w]+l(w,j) //判断经过w点到j点的路径是比原来的路径D[j]更短后,对j点的路径进行优化
If QM[j]==0 then //只有d[j]的最短路径变化了,j后面的路径才可能会变短。(注意是可能会,尽管如此,我们也要让它入列)
Begin
Queue<-j;
QM[j]=1; //当j点不在队列里,j入队,并且将QM[j]标志置为1表示j已入队
End
End
End;
9 for each v in V do
begin
write(D[v]);
end; //优化完成后,D数组存放的就是从源点到各点的最短路径值,可以输出结果
10 end.
举个例子:
一个有向图G ,
1到2权值为5
1->3 7
2->3 -2
2->4 3
3->4 4
求源点vo到各顶点的最短路径
代码如下:
#include <iostream>
#include <vector>
#include <queue>
#define mx 99999
#define vmax 20
using namespace std;
int vn ;
int v0 ;
typedef struct
{
int v , w;
}edge;
vector<edge>g[20] ;
int dist[vmax] ;
int visited[vmax] ;
void spfa()
{
for(int i = 1; i <= vn; i ++)
dist[i] = mx ;
dist[v0] = 0 ;
queue<int>s ;
visited[v0] = 1 ;
s.push(v0);
while(!s.empty())
{
int u = s.front();
s.pop();
int temp ;
visited[u] = 0 ;
for(int i = 0; i < (int)g[u].size(); i ++)
{
temp = g[u][i].w ;
if(dist[u] != mx && dist[g[u][i].v] > dist[u] + temp)
{
dist[g[u][i].v] = dist[u] + temp ;
if(visited[g[u][i].v] == 0)
{
s.push(g[u][i].v) ;
visited[g[u][i].v] = 1 ;
}
}
}
}
}
int main()
{
int m , weight, x , y;
cin >> vn >> m ;
edge temp ;
for(int i = 0; i < m; i ++)
{
cin >> x >> y >> weight ;
temp.v = y ;
temp.w = weight ;
g[x].push_back(temp);
}
int t ;
cin >> v0 ;
spfa();
while(cin >> t )
{
cout << dist[t] << endl ;
}
return 0 ;
}
假若1为源点结果为
dist[2] = 5
dist[3] = 3
dist[4] = 7
spfa算法还有优化要。大家一起努力吧!