概述
SPFA算法
引子
在很早很早以前,有个IOer兼BLOGer挖了一个坑:有一类问题,它要求我们从图上某点出发,走到另一点,求最短路径。
long long ago;
我们学习了三种求最短路径的方法:
- Floyd算法
- Dijkstra算法
- Bellman-Ford算法
(如果不了解,可以点开上面的超链接看看(尽管我写的不好))
Floyd本质上是动态规划,Dijkstra算法本质上是贪心或者bfs,而Bellman-Ford的思路是枚举边,花了很多时间在没用的边上。能不能优化一下呢?
这就引出了我们今天的内容,SPFA算法。
算法的提出和背景
(虽然了解这些东西不会提高分数,但是可以学习计算机学家们的严谨精神,至少可以装装13)
SPFA的全称是Shortest Path Faster Algorithm,意思是更快的最短路径算法。由西南交通大学段凡丁于1994年提出。
看看SPFA的名字,我们看到它口气真大,居然说自己更快,我们来看看它为什么更快。
算法的原理
在图G(V,E)(V为点集,E为边集)中,先用一个数组Dis[i]表示源点s到顶点i的最短路径经。初始化Dis清为∞,Dis[s]=0。
算法大致流程就是:
- 新建一个队列Q,将s入队
- 取队首元素,松弛它的临界点,如果松弛成功,则该临界点入队。(松弛操作见Bellman-Ford算法)
- 当前队首元素出队。
- 如果队空,则找到最短路径,否则重复操作2。
算法正确性证明于时间复杂度分析
这是它的正确性的证明:
(摘自百度百科)
每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的Dis值就是对应结点的最短路径值。
它的时间复杂度号称是O(me),m是平均每点入队次数(注意,每点可能不止入队一次,可能经过松弛的点又会入队),有人证明说m一般不会超过2,但这个证明是十分不严谨的,并且SPFA算法十分不稳定,因此国际上一般不承认SPFA算法。国内有些老师也不会教。
负环判断
如果一个点入队次数超过n次,那么图有负环。
代码实现(伪代码)
int map[MAX][MAX];
int dis[MAX]; //MAX为总点数
bool vis[MAX];
int num[MAX];//记录每个结点入队的次数
struct cmp
{
bool operator()(int x,int y)
{
return x>y;
}
};
bool SPFA(int s0,int n)
{
priority_queue<int,vector<int>,cmp> q;
memset(vis,false,sizeof(vis));
memset(num,0,sizeof(num));
for(int i=0;i<n;i++)
dis[i] = INF;
dis[s0] = 0;
q.push(s0);
vis[s0] = true;
num[s0]++;
while(!q.empty())
{
int p = q.top();
q.pop();
for(int i=0;i<n;i++)
{
if(dis[p]+map[p][i]<dis[i])
{
dis[i] = dis[p]+map[p][i];
if(!vis[i])
{
q.push(i);
num[i]++;
if(num[i]>n)//存在负环
{
return false;
}
vis[i]=true;
}
}
}
vis[p] = false;
}
return true;
}
代码转自:http://blog.csdn.net/runninghui/article/details/8895586
最后
以上就是欣喜吐司为你收集整理的【原创】求最短路径-SPFA算法SPFA算法的全部内容,希望文章能够帮你解决【原创】求最短路径-SPFA算法SPFA算法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复