最短路SPFA算法 (通过邻接表来实现)

来源:互联网 发布:拒绝退款后淘宝介入 编辑:程序博客网 时间:2024/06/05 02:56

适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。

主要思路:现将图用邻接表edges[mxan]来存储,(当然邻接矩阵也可以),然后进行SPFA操作,用dis[maxm]数组来记录源点到其他点的最短路,用vis[maxm]数组来标记哪些点已经被走过,用que[maxm]队列来维护当前要待松弛的点,每次取队首进行操作,由队首扩展出的点依次加入队尾,如此反复,直到队列为空为止。

实现方法

  建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点作为起始点去刷新到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。

判断有无负环
  如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

下面贴代码:

#include <iostream>using namespace std;#define INF 0x7fffffffconst int maxn = 100005;const int maxm = 5005<<1;struct Data{    int x;    int value;    int next;}edges[maxm];int first[maxm], dis[maxm], que[maxm], vis[maxm];int N, M;void read_edges(){    cin >> N >> M;    for(int i = 1; i <= N; i++){        first[i] = -1;        vis[i] = 0;        dis[i] = INF;    }    for(int e = 1, u, v, w; e <= M; e++){        cin >> u >> v >> w;        edges[e].x = v;        edges[e].value = w;        edges[e].next = first[u];        first[u] = e;    }}void SPFA(){    int start = 1; //此个节点可以是任意节点,从题目中推出    vis[start] = 1;    dis[start] = 0;    int head = 0, tail = 1, cur;    que[tail] = start; //第一个节点插入到队尾     while(head != tail){        head = (head + 1) % 1000; //head向后扩展一位以进行下一个节点的操作                                  //%1000可能是个小优化,相当于是一个滚动队列         cur = que[head];        vis[cur] = 0; //首先标记当前节点是没有走过的         int k = first[cur]; //当前节点所对应的第一条边,可以扩展出其他的所有边         while(k != -1){            if(dis[cur] > dis[edges[k].x] + edges[k].value) //松弛操作                 dis[cur] = dis[edges[k].x] + edges[k].value;            if(!vis[edges[k].x]){                vis[edges[k].x] = 1;                tail = (tail + 1) % 1000;                que[tail] = edges[k].x; //把当前节点插入队尾             }            k = edges[k].next; //扩展出其他的所有边         }    }//  cout << dis[N] << endl; }int main(){    read_edges();    SPFA();    return 0;}
原创粉丝点击