最潮最短路算法:SPFA

来源:互联网 发布:matlab字符分割算法 编辑:程序博客网 时间:2024/06/08 18:40

首先理解单源最短路一滴概念(以下概念不作证明,证明自行baidu):

 

V:点集

 

E:边集

 

s:源点

 

u:当前起始定点

 

v:当前目标顶点

 

u->v:表示顶点u直接到达顶点v(经过边数==1)

 

u~>v:表示顶点u通过某一路径到达顶点v(经过边数>=1)

 

lo[u, v]:表示顶点u到顶点v既最短路径

 

d[v]:储存源点s到顶点v既最短路径估计值,下界就系lo[u, v]

 

w[u,v]:从u到v既一条直达路径既长度(边权)

 

lo[s, v] <= lo[s, u] + w[u, v]:三角不等式1

 

d[v] > d[u] + w[u, v]:三角不等式2

 

P<v0, v1, ......vk>:顶点v0到顶点vk既最短路径点集

 

 

松弛技术:当三角不等式2未到达下界(即等于)时,可以对d[v]进行松弛,即执行d[v] = d[u] + w[u,v]

 

 

松弛路径性质:P<v0, v1, v2, ........vk>为s到u既最短路径点集(v0为s, vk为u),如果p按照边(v0, v1), (v1, v2)..(vk - 1, vk)顺序进行松弛,呢个时候d[vk] = lo[s, vk]。

 

-------------------------------------------------------银蛋阉割线---------------------------------------------------------------

 

SPFA(Shoetest-Path-Faster-Algorithm),时间复杂度底编程复杂度超低,速度更加系唔系人甘品。

 

作为一种单源最短路算法,潮就潮在,功能上可以代替传统Dijkstra同埋Bellman-Ford。

 

点解可以甘样讲?

 

(以下对比前提:链接表储存方式)

 

传统Dijkstra通过两重循环,对每一个当前d[u]最小者连接既边进行松弛,每个点仅执行一次,呢个算法费时就费在每次揾d[u]最小者都要遍历一次成个点集V,即系费时O(|V|),仲有Dijkstra吾可以用于有负回路既图上面。

 

Bellman-Ford速度明显最慢,因为该算法硬性对每一条边进行|V| - 1次松弛检测,不过呢个算法可以系有负回路既图上用,因为呢个算法既核心系松弛路径性质,每条最短路最多有|V| - 1条边,所以每条边最多松弛|V| - 1次,如果进行第|V|次松弛检测既时候有一条边可以被松弛,就说明有负回路存在,所以可以判断差分约束系统有无解。

 

SPFA算法系采用队列同埋松弛技术,每次从队列头取出一点u,对u可直达既点v进行松弛检测,如果松弛成功,检查点v系唔系已经入队列,如果未入,将v压入队列尾,进行以上操作直到队列为空或者有一点进入队列次数为|V|。

SPFA可以代替传统Dijkstra系因为每次只将被松弛点放入队列,当无点被松弛既时候,即每一个d[u] == lo[s, u]。极大程度上减少不必要既松弛操作,速度比传统Dijkstra快。

 

代替Bellman-Ford系因为SPFA同样适用于路径松弛性质,当有一点进入队列次数为|V|既时候,同样可以证明有负回路存在。

 

 

下面比出无负回路图SPFA代码加注释:


#include <iostream>#include <vector>#include <cstdio>#include <cstdlib>#include <queue>#include <memory.h>using namespace std;#define MAXI 1011#define INFI 0x7fffffff#define LLD long longstruct V{    int v, w;//v, w[u, v]};vector<V> g[MAXI]; //邻接表int n, dis[MAXI];//|V|, d[]bool inQ[MAXI];//入队标记

void SPFA(int u)//源点{    int i, v, w;    queue<int> Q;//队列    for (i = 0; i < n; i++)//初始化    {        dis[i] = INFI;        inQ[i] = false;    }    Q.push(u);//压入源点    inQ[u] = true;    dis[u] = 0;    while (!Q.empty())//队列不为空    {        u = Q.front();        Q.pop();        inQ[u] = false;        for (i = 0; i < g[u].size(); i++)        {            v = g[u][i].v;            w = g[u][i].w;            if (dis[v] - w > dis[u])//符合三角不等式2            {                dis[v] = w + dis[u];//松弛操作                if (inQ[v] == false)//判断v系唔系队列                {                    Q.push(v);//唔系就入队列                    inQ[v] = true;                }            }        }    }}

下面比一个例子:

 

 
 

 

 

 

 

就系甘样,其实就系大家非常熟悉既BFS,另外再加上松弛技术姐嘛。

 

负回路版本只需要加上一个数组记录每个点入队次数就得。

 

多谢收睇~~



原创粉丝点击