Bellman-Ford算法的改进:SPFA算法

来源:互联网 发布:剑三大师脸型数据 编辑:程序博客网 时间:2024/05/17 08:40

一、算法简介

SPFA算法全称是最短路径快速算法(Shortest Path Faster Algorithm),是基于Bellman-Ford算法的队列优化。SPFA算法能和Bellman-Ford算法一样,可以处理带负权值边的图,但是复杂度要小很多。

一般认为,此算法的时间复杂度为O(ke),其中k为所有顶尖进队的平均次数,可以证明k一般不超过2。

原来的Bellman-Ford算法,对图进行 |V|-1 次遍历,多了很多没有必要的操作。现在考虑用一个队列来优化,若顶点v的最短路径改变了,则将此顶点入队。下一次遍历从该顶点出发到所有邻接顶点的边,进行松弛操作,也只将更新了最短路径的边入队。


二、伪代码实现

wikipedia的版本

procedure Shortest-Path-Faster-Algorithm(G, s)  1    for each vertex v ≠ s in V(G)  2        d(v) := ∞  3    d(s) := 0  4    offer s into Q  5    while Q is not empty  6        u := poll Q  7        for each edge (u, v) in E(G)  8            if d(u) + w(u, v) < d(v) then  9                d(v) := d(u) + w(u, v) 10                if v is not in Q then 11                    offer v into Q

三、C++实现

/* * @ spfa.cpp * @authorHalfish Zhang * @version1.0  2014/5/24 */#include <iostream>#include <queue>#include <vector>#include <stack>using namespace std;// 最大的顶点数const int MAX_NUM = 2014;// 定义常量无穷大const int INF_NUM = 0x3f3f3f3f;// 用邻接链表来存储边struct Edge{int v;int weight;};// 邻接表用vector来存储比较合适vector<Edge> edges[MAX_NUM * 2];int vertexNum, edgeNum;// 实际的顶点数和边数int source;// 源点int dist[MAX_NUM], pred[MAX_NUM];// dist[i]用来存放从 source 到 i 的最短距离,pred[i]为最短路径的前驱bool inQueue[MAX_NUM]; // inQueue[i]存放顶点 i 是否在队列中int pushCount[MAX_NUM]; // pushCount[i] 表示顶点 i 被放进队列的次数queue<int> q;bool spfa(){// initfor(int i = 1; i <= vertexNum; ++ i){dist[i]= INF_NUM;pred[i] = -1;inQueue[i] = false;pushCount[i] = 0;}dist[source] = 0;// push source into queueq.push(source);inQueue[source] = true;pushCount[source] = 1;while(!q.empty()){// cur means current vertexint curr = q.front();q.pop();inQueue[curr] = false;// edges[curr]是一个vector,里面放着和curr相邻的边Edge。 int len = edges[curr].size();//共len个顶点与curr相邻for(int i = 0; i < len; ++ i){Edge *edges_vec = &edges[curr][i]; // 用edges_vec表示第i条边,便于寻址// 松弛操作 relaxif(dist[(*edges_vec).v] > dist[curr] + (*edges_vec).weight ){dist[(*edges_vec).v] = dist[curr] + (*edges_vec).weight;pred[(*edges_vec).v] = curr;// 若不在队列中,需要加入队列if(!inQueue[(*edges_vec).v]){q.push((*edges_vec).v);inQueue[(*edges_vec).v] = true;pushCount[(*edges_vec).v] ++;// 如果入队列的次数超过vertexNum,说明存在负权值cycleif(pushCount[(*edges_vec).v] > vertexNum){// 释放内存?while(!q.empty()) {q.pop();}return false;}}}}}return true;}void print(int i){cout << "The path from " << source << " to " << i << " is : " << endl;stack<int> s;s.push(i);while(s.top() != source) {s.push(pred[s.top()]);}int len = s.size();for(int i = 0; i < len; ++ i) {cout << s.top() << " ";s.pop();}cout << endl << endl;}int main(int argc, char const *argv[]){cout << "Please input the vertexNum, edgeNum and source:" << endl;cin >> vertexNum >> edgeNum >> source;cout << "Please input the " << edgeNum ;cout << " edges, which from u to v with weight of w" << endl;int u1, v1, w1;Edge temp;for(int i = 0; i < edgeNum; ++ i) {cout << i + 1 << " :  ";cin >> u1 >> v1 >> w1;temp.v = v1;temp.weight = w1;edges[u1].push_back(temp);}if( spfa() ){cout << endl << endl;cout << "The minimum weight of these paths from " << source << " are: " << endl;for(int i = 1; i <= vertexNum; ++ i)cout << dist[i] << " ";cout << endl << endl;cout << "All these paths are as follows: " << endl;for(int i = 1; i <= vertexNum; ++ i)print(i);} else {cout << "Sorry, no shortest path exist, ";cout << "because there are some nagetive cycles" << endl;}return 0;}

四、测试样例

见下图


输入样例:

6 9 1
1 2 7
1 3 9
1 6 14
2 3 10
2 4 15
3 4 11
3 6 2
4 5 6
5 6 9

输出样例

The minimum weight of these paths from 1 are:
0 7 9 20 26 11


All these paths are as follows:
The path from 1 to 1 is :
1


The path from 1 to 2 is :
1 2


The path from 1 to 3 is :
1 3


The path from 1 to 4 is :
1 3 4


The path from 1 to 5 is :
1 3 4 5


The path from 1 to 6 is :
1 3 6




--------------------------------
Process exited with return value 0
Press any key to continue . . .

0 0
原创粉丝点击