SPFA(Shortest Path Faster Algorithm)算法实现类

继续图论的单源最短路径算法实现,上次学习了Bellman-Ford算法,知道了它的复杂度比较高,主要消耗在松弛操作有点冗余。所以,就有人对其进行优化,西南交通的段凡工前辈提出了这个SPEA算法。在Wiki上看到,有人把其称为Shortest Path Finding Algorithm,也不知道具体是怎样的,不过看在其速度蛮快的份上,我就用Faster了。复杂度快达O(kE)。

其中,优化在哪里呢,如下:

SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,算法大致流程是用一个队列来进行维护,即用一个先进先出的队列来存放被成功松弛的顶点。初始时,源点s入队。当队列不为空时,取出队首顶点,对它的邻接点进行松弛。如果某个邻接点松弛成功,且该邻接点不在队列中,则将其入队。经过有限次的松弛操作后,队列将为空,算法结束。SPFA算法的实现,需要用到一个先进先出的队列 queue 和一个指示顶点是否在队列中的标记数组mark。为了方便查找某个顶点的邻接点,提高效率,图采用临界表存储。

Wiki上谈到了SPEA的优化,如下:

SPFA算法有两个优化算法 SLF 和 LLL: SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾。 LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。 在实际的应用中SPFA的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法。

废话不多说,代码如下:

#include <iostream>
#include <queue>

//Vertex number no more than MAX_INT, because ip is int tpye in initialize.

#define MAXEDGE 100
#define MAXVERTEX 15
#define MAX_INT 0x7FFFFFFF

#define AL_END -1

using namespace std;

//For adjacency list.
struct Edge 
{
	int next;
	int vertex;
	long weight;
};

class AlGraph
{
public:
	int source;
	int edgeNum;
	long distance[MAXVERTEX];
	bool hasNegativeCycle;
	void spea();

private:
	Edge edge[MAXEDGE];				//For adjacency list.
	int alVertex[MAXVERTEX];		//For adjacency list.
	bool inQueue[MAXVERTEX];		//For fast speed.
	queue<int> spfaQueue;

	int inQueueTimes[MAXVERTEX];	//To tests negative cycles.

	void initialize();
};

void AlGraph::initialize()
{
	hasNegativeCycle = false;

	memset(alVertex, AL_END, sizeof(alVertex));
	memset(inQueue, 0, sizeof(inQueue));
	memset(inQueueTimes, 0, sizeof(inQueueTimes));
	fill(distance, distance + MAXVERTEX, MAX_INT);

	while (!spfaQueue.empty())
	{
		spfaQueue.pop();
	}

	int ip = 0;
	int source;
	int destination;
	int weight;
	for (int i = 0; i < edgeNum; i++)
	{
		cin >> source >> destination >> weight;

		edge[ip].next = alVertex[source];
		edge[ip].vertex = destination;
		edge[ip].weight = weight;
		alVertex[source] = ip++;

		/*
		//For undirected graph.
		edge[ip].next = alVertex[destination];
		edge[ip].vertex = source;
		edge[ip].weight = weight;
		alVertex[destination] = ip++;
		*/
	}

}

void AlGraph::spea()
{
	initialize();

	distance[source] = 0;
	spfaQueue.push(source);
	inQueueTimes[source]++;
	inQueue[source] = true;

	while (!hasNegativeCycle && !spfaQueue.empty())
	{
		int nowvertex = spfaQueue.front();
		spfaQueue.pop();
		inQueue[nowvertex] = false;

		long a;
		long b;
		for (int i = alVertex[nowvertex]; i != AL_END; i = edge[i].next)
		{
			a = distance[edge[i].vertex];
			b = distance[nowvertex] + edge[i].weight;
			if (b < a)
			{
				distance[edge[i].vertex] = b;
				if (!inQueue[edge[i].vertex])
				{
					spfaQueue.push(edge[i].vertex);
					inQueue[edge[i].vertex] = true;
				}
				inQueueTimes[edge[i].vertex]++;
				if (inQueueTimes[edge[i].vertex] > edgeNum)
				{
					hasNegativeCycle = true;
				}
			}
		}
	}
}

int main()
{
	AlGraph test;

	cin >> test.source >> test.edgeNum;

	test.spea();

	if (!test.hasNegativeCycle)
	{
		for (int i = 0; i < MAXVERTEX; i++)
		{
			cout << "Distance to  " << i << " is: " << test.distance[i] << endl;
		}
	}

	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值