最潮最短路算法:SPFA

首先理解单源最短路一滴概念(以下概念不作证明,证明自行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 long

struct 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,另外再加上松弛技术姐嘛。

 

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

 

多谢收睇~~



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值