Bellman-Ford算法的队列优化

Dijkstra算法求最短路径的图时不能有负权边,因为扩展到负权边的时候会产生更短的路径,有可就已经破坏了已经更新的点路程不会改变的性质。Dijkstra算法虽然好,具有良好的扩展性,扩展后可以适应很多问题,高效的A*算法就是有Dijkstra算法扩展来的,但Dijkstra算法无法解决负权边,这是最大的弊端。

Bellman-Ford算法正好弥补Dijkstra算法的这个弊端,可以堪称完美最短路径算法。

算法大致如下:每次选取队头顶点u,对顶点u的所有出边进行松弛操作。例如有一条边 u->v 的边,如果通过 u->v 这条边使得源点到顶点v的最短路径变短(dis[u]+e[u][v]<dis[v]),且顶点v不在当前的队列中,就将顶点v放入队尾。需要一个数组里判断哪些点已经在队列中。在对顶点u的所有出边松弛完毕后,就将顶点u出队。接下来不断从队列中取出新的队头顶点在进行如上操作,直至队列空为止。

代码:

#include <iostream>
#include <vector>
#include <queue>

const int MAX=999999;

int main()
{
	int n, m;  //n表示顶点个数,m表示边数
	std::cin >> n >> m;

	std::vector<int>u(m + 1);  //起点集合
	std::vector<int>v(m + 1);  //末点集合
	std::vector<int>w(m + 1);  //权值集合
	std::vector<int>first(n + 1); //存储首到尾号顶点的第一条边的编号
	std::vector<int>next(m + 1);  //存储当前编号下的边的下一条边的编号
	std::vector<int>dis(n + 1);  //存储顶点
	std::vector<int>book(n + 1);  //标记

	std::queue<int>que;  //维护队列

	//初始化一波
	for (int i = 1; i <= n; i++)
	{
		dis[i] = MAX;
		book[i] = 0;
		first[i] = -1;
	}

	dis[1] = 0;   //初始化首顶点

	for (int i = 1; i <= m; i++)
	{
		std::cin >> u[i] >> v[i] >> w[i];

		//建立邻接表
		next[i] = first[u[i]];
		first[u[i]] = i;
	}

	que.push(1);   //首号顶点入队
	book[1] = 1;

	while (!que.empty())
	{
		int k = first[que.front()];   //当前需要处理的队首顶点

		//扫描当前顶点所有的边
		while (k != -1)
		{
			if (dis[v[k]] > dis[u[k]] + w[k])
			{
				dis[v[k]] = dis[u[k]] + w[k];
				if (book[v[k]] == 0)
				{
					que.push(v[k]);
					book[v[k]] = 1;
				}
			}
			k = next[k];
		}
		
		que.pop();  //出队
	}

	for (int i = 1; i <= n; i++)
	{
		std::cout << dis[i] << std::endl;
	}

	::getchar(); ::getchar();
	return 0;
}

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