SPFA算法
求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm。 最短路径快速算法-SPFA算法是西南交通大学段凡丁于1994年发表的。
适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
算法思想:我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
标程一:
const int INF = 999999;
int
map
[
MAXN
][
MAXN
]
;
//map[i,j]
为初始输入的
i
到
j
的距离,未知的
map[i,j]=INF;
int
dis
[
MAXN
]
;
//源点
S
到
i
的最短路
char
vst
[
MAXN
]
;
//是否在队列中的标记
int Q[MAXN];
//
队列
//
参数
n
表示结点数,
s
表示源点
int
SPFA
(
int
n,
int
s
)
{
int
i, pri, end, p, t
;
// pri
是队列头结点,
end
是队列尾结点
memset
(
vst,
0
,
sizeof
(
vst
))
;
//初始化
for
(
int
i
=
0
;
i
<</b>
MAXN
;
++
i
)
Q
[
i
]
=
0
;
//初始化队列为空
for
(
i
=
0
;
i
<</b>
n
;
i
++
)
dis
[
i
]
=
INF
;
//初始化源点到
I
的值为最大值
dis
[
s
]
=
0
;
//源点为
0
vst
[
s
]
=
1
;
//标记为已入队
Q
[
0
]
=
s
;//源点入队
pri
=
0
;
end
=
1
;
//队首队尾赋值
while
(
pri
<</b>
end
)
{
p
=
Q
[
pri
]
;
//取队首元素
for
(
i
=
0
;
i
<</b>
n
;
++
i
)
//
更新
dis
{
if
(
dis
[
p
]
+
map
[
p
][
i
]
<</b>
dis
[
i
])
{
dis
[
i
]
=
dis
[
p
]
+
map
[
p
][
i
]
;
if
(
!
vst
[
i
])
//
未在队列中
{
Q
[
end
++
]
=
i
;
vst
[
i
]
=
1
;
}
}
}
vst
[
p
]
=
0
;
//
置出队的点为未标记
pri
++
;
}
return
1
;
}
标程二:
int
num[999999]; //记录入队次数
void
spfa(int
s) // 初始结点s,即为起点,若目标结点t,则输出dict[t]。
{ init_data(s);
int
head = 0, tail = 1;
int path[Max]; // 可增加一个path[]数组来记录最后s到t的路径。
queue[0] = s; //que.push(s);
dict[s] = 0;
while
(tail > head)//(!que.empty())
{ int
u = queue[head];//int u=que.front(); //que.pop();
vis[u] = true;
for
(i = 1; i <= n; i ++)
{ if
(dict[i] > dict[u] + edge[u][i])
{ dict[i] = dict[u] + edge[u][i];
path[i] = u;
num[i]++
if(num[i]>=n) return
1;//判断是否有负权值……
if
(!vis[i]) // 对以前没有访问过的顶点,加入队列中。
{ vis[i] = true;
queue[tail] = i;// que.push(i);
tail ++;
}
}
}
vis[u] = false; // 记得把出队列的顶点的vis[]置为false。
head ++;
}
}
判断负权回路 num[i]>=n的原因,即使所有的点更新都会让i入队的话,才只有n-1次,这时一定是最小值了,入队n次,一定有负权回路