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

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

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

实现方法

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

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

下面贴代码:

#include 

using namespace std;

#define INF 0x7fffffff

const 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;
}

你可能感兴趣的:(须长记忆)