SPFA算法

SPFA算法

用途

用于解决单源最短路径问题,可以处理含有负边权边的图(若存在源点可达负环则无解)。

算法描述

SPFA算法是在Bellman-Ford算法基础上改进得来的 (关于BF算法可参考 《Bellman-Ford算法》)。BF算法时间复杂度较高,原因在于每一次循环都需要遍历图中所有边,其中包含了大量的重复遍历,这就会使算法效率大大降低。SPFA算法依据的原理是:只有当某个结点到源点的最短距离d[i]改变后,由结点i出发可达的结点j与源点的最短距离d[j]才可能会被改变。由此可以对BF算法进行优化得到期望时间复杂度为 O ( k E ) O(kE) O(kE) (k为常数,E为边数)的SPFA算法,其主要步骤如下:

  1. 建立队列q,将源点入队;
  2. 队首结点u出队,遍历u的所有出边 u->v 进行松弛操作,如果 d[u] + length[u->v] < d[v], 则用较小值更新d[v],若此时v不在队列中,则将其入队;
  3. 当队列非空时,重复步骤2,同时每次操作要判断是否有某个结点的入队次数大于 n - 1,若有则说明图中存在从源点可达的负环,结束算法并返回false;
  4. 所有最短距离优化完毕,结束算法并返回true。

代码实现

const int maxn = 1000;
const int INF = 1000000000;

struct node
{
    int v, l;
};

vector<node> adjL[maxn];        // 邻接表
int n;                          // 结点个数
int d[maxn];                    // 结点i到源点的最短距离
int num[maxn];                  // 结点i的入队次数
bool inq[maxn];                 // 结点i是否已入队

bool SPFA(int s)
{
    for (int i = 0; i < n; i++)         // 初始化
    {
        num[i] = 0;         
        d[i] = INF;
        inq[i] = false;
    }
    d[s] = 0;               // 源点到自身距离为0
    
    queue<int> q;
    q.push(s);              // 源点入队
    num[s]++;
    inq[s] = true;
    
    while (!q.empty())      // 队非空一直循环
    {
        int u = q.front();          // 队首结点出队
        q.pop();
        inq[u] = false;             
        
        for (int i = 0; i < adjL[u].size(); i++)        // 遍历所有出边
        {
            int v = adjL[u][i].v;
            int l = adjL[u][i].l;
            if (d[u] + l < d[v])        // 松弛
            {
                d[v] = d[u] + l;
                if (!inq[v])
                {
                    q.push(v);
                    inq[v] = true;
                    num[v]++;
                    if (num[v] > n - 1)         // 入队次数超过 n - 1,说明存在源点可达负环,返回false
                        return false;
                }
            }
        }
    }
    return true;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值