SPFA 算法介绍和实现

一 点睛

SPFA 算法是 Bellman-Ford 算法的队列优化算法,通常用于求解负权边的单源最短路径,以及判断负环。在最坏的情况下,SPFA 算法的时间复杂度和 Bellman-Ford 算法相同,为O(nm);但在系数图上运行效率较高,为 O(km),其中 k 是一个较小的常数。

二 算法步骤

1 创建一个队列,首先源点 u 入队,标记 u 在队列中,u 的入队次数加1。

2 松弛操作。取出队头节点 x,标记 x 不在队列中。扫描所有 x 的所有出边 i(x,v,w),如果 dis[v]>dis[x]+e[i].w,令  dis[v]=dis[x]+e[i].w。如果节点 v 不在队列中,判断 v 的入队此时加1后大于或等于 n,则说明有负环,退出;否则 v 入队,标记 v 在队列中。

3 重复松弛操作,直到队列为空。

三 算法实现

package graph.spfa;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Spfa {
    static final int maxn = 505;
    static final int maxe = 100001;
    static int n;
    static int m;
    static int cnt;
    static node e[] = new node[maxn];
    static int head[] = new int[maxn];
    static int dis[] = new int[maxn];
    static int sum[] = new int[maxn];
    static boolean vis[] = new boolean[maxn];

    static {
        for (int i = 0; i < e.length; i++) {
            e[i] = new node();
        }

        for (int i = 0; i < head.length; i++) {
            head[i] = -1;
        }
    }

    static void add(int u, int v, int w) {
        e[cnt].to = v;
        e[cnt].next = head[u];
        e[cnt].w = w;
        head[u] = cnt++;
    }

    static boolean spfa(int u) {
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < vis.length; i++) {
            vis[i] = false;
        }
        // 统计入队的次数
        for (int i = 0; i < sum.length; i++) {
            sum[i] = 0;
        }
        for (int i = 0; i < dis.length; i++) {
            dis[i] = 0x3f;
        }

        vis[u] = true;
        dis[u] = 0;
        sum[u]++;
        q.add(u);
        while (!q.isEmpty()) {
            int x = q.peek();
            q.poll();
            vis[x] = false;
            for (int i = head[x]; i >= 0; i = e[i].next) {
                int v = e[i].to;
                if (dis[v] > dis[x] + e[i].w) {
                    dis[v] = dis[x] + e[i].w;
                    if (!vis[v]) {
                        if (++sum[v] >= n)
                            return true;
                        vis[v] = true;
                        q.add(v);
                    }
                }
            }
        }
        return false;
    }

    static void print() { // 输出源点到其它节点的最短距离
        System.out.println("最短距离:");
        for (int i = 1; i <= n; i++)
            System.out.print(dis[i] + " ");


        System.out.println();
    }

    public static void main(String[] args) {
        cnt = 0;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        m = scanner.nextInt();

        int u, v, w;
        for (int i = 1; i <= m; i++) {
            u = scanner.nextInt();
            v = scanner.nextInt();
            w = scanner.nextInt();
            add(u, v, w);
        }
        if (spfa(1))
            System.out.println("有负环!");
        else
            print();
    }
}

class node {
    int to;
    int w;
    int next;
}

四 测试

1 无负环情况

2 有负环情况

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SPFA算法是一种基于Bellman-Ford算法的单源最短路径算法,其主要思想是通过队列来遍历节点,不断更新节点的距离值,直到所有节点的距离值都不再发生变化为止。以下是SPFA算法的C语言实现代码: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAX_N 1000 #define INF 0x7fffffff typedef struct edge { int to; int w; struct edge *next; } Edge; Edge *G[MAX_N + 1]; int dist[MAX_N + 1]; bool in_queue[MAX_N + 1]; void add_edge(int u, int v, int w) { Edge *e = (Edge *)malloc(sizeof(Edge)); e->to = v; e->w = w; e->next = G[u]; G[u] = e; } void spfa(int s, int n) { for (int i = 1; i <= n; i++) { dist[i] = INF; in_queue[i] = false; } dist[s] = 0; in_queue[s] = true; int q[MAX_N], head = 0, tail = 1; q[head] = s; while (head != tail) { int u = q[head++]; if (head >= MAX_N) { head = 0; } in_queue[u] = false; for (Edge *e = G[u]; e != NULL; e = e->next) { int v = e->to; if (dist[v] > dist[u] + e->w) { dist[v] = dist[u] + e->w; if (!in_queue[v]) { q[tail++] = v; if (tail >= MAX_N) { tail = 0; } in_queue[v] = true; } } } } } int main() { int n, m, s; scanf("%d%d%d", &n, &m, &s); for (int i = 0; i < m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); add_edge(u, v, w); } spfa(s, n); for (int i = 1; i <= n; i++) { printf("%d ", dist[i]); } printf("\n"); return 0; } ``` 其中,`G`是邻接表,存储图的信息;`dist`是从源点到各个节点的最短距离数组;`in_queue`记录每个节点是否在队列中。`add_edge`函数用于添加边,`spfa`函数实现SPFA算法的主体部分,`main`函数用于读入图的信息,调用`spfa`函数求解最短路径,并输出结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值