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

一、算法简介

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
 * @author		Halfish Zhang
 * @version		1.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()
{
	// init
	for(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 queue
	q.push(source);
	inQueue[source] = true;
	pushCount[source] = 1;

	while(!q.empty())
	{
		// cur means current vertex
		int 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条边,便于寻址

			// 松弛操作 relax
			if(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,说明存在负权值cycle
					if(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;
}

四、测试样例

见下图

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

输入样例:

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 . . .

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/zhangxb35/article/details/26953409
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞