SPFA 算法介绍和实现

x33g5p2x  于2022-07-10 转载在 其他  
字(1.8k)|赞(0)|评价(0)|浏览(269)

一 点睛

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 有负环情况

相关文章