最短路径(Bellman-Ford的优化)

每次选取队首顶点u,对顶点u的所有出边进行松弛操作
例如有一条u→v的边,如果通过u→v这条边使得源点到顶点v的最短路径变短,且顶点v不在当前的队列中,就将顶点v放入队尾。
同一个顶点同时在队列中出现多次是毫无意义的,因此需要一个数组来判重
在对顶点u的所有边松弛完毕后,酒店顶点v出队
接下来不断从队列中取出新的队首顶点再进行如上操作,直到队列空为止

初始时将源点加入队列。
每次从队首取出一个点,并对与其相邻的所有顶点进行松弛尝试,若某个相邻的顶点松弛成功,且这个相邻的顶点不在队列中,则将它加入到队列。
对当前顶点处理完毕后立即出队,并对下一个新队首进行如上操作,直到队列为空时,算法结束
这个Bellman-Ford算法与BFS有些相似,但是与BFS不同的是,一个顶点很可能出队后又再次被放入队列
即:
当一个顶点的最短路径估计值变小之后,需要对其所有出边进行松弛,但是如果这个顶点的最短路径估计值再次变小,仍需要对其所有出边进行再次松弛,这样才能保证相邻顶点的最短路程值同步更新

最坏时间复杂度:
O(NM)

通过队列优化的Bellman-Ford算法如何判断一个图是否具有负环?
如果某个点进入队列的次数超过了n次,那么这个图肯定存在负环

用队列优化的Bellman-Ford算法的关键之处:
只有在前一遍松弛中改变了最短路程估计值的顶点,才可能引起他们邻接点最短路程估计值发生改变。
因此用一个队列来存放那些被成功松弛的顶点,之后只对队列中的点进行处理

完整代码

#include<stdio.h>
int main()
{
	int n, m, i, j, k;
	int u[8], v[8], w[8];
	//u,v,w的数组大小要比m的最大值大1
	int first[6], next[8];
	//first要比n的最大值大1,next要比m的最大值大1

	int dis[6] = { 0 };	//源点到各个点的最短路径
	int book[6] = { 0 };	//book用于记录哪些顶点存放在queue中
	int que[101] = { 0 };	//定义一个队列
	int head = 1, tail = 1;	//初始化队首和队尾

	int inf = 9999999;	//定义无穷

	scanf("%d%d", &n, &m);

	for (i = 1; i <= n; i++)	//初始化dis的值
	{
		dis[i] = inf;
	}
	dis[1] = 0;	//把源点到自身设置为0

	for (i = 1; i <= n; i++)	//初始化book,表示点均不在队列中
		book[i] = 0;

	for (i = 1; i <= n; i++)	//初始话first,表示各个点都没有边
		first[i] = -1;

	for (i = 1; i <= m; i++)
	{
		scanf("%d%d", &u[i], &v[i], &w[i]);	
		//输入各个边的始点,终点,权值
		next[i] = first[u[i]];	//建立邻接表核心语句
		first[u[i]] = i;
	}

	que[tail] = 1;	//将源点入队
	tail++;
	book[1] = 1;	//标记源点已在队列中

	while (head < tail)
	{
		k = first[que[head]];	//取得当前要处理的队首顶点
		while (k != -1)
		{
			if (dis[v[k]] > dis[u[k]] + w[k])	//Bellman-Ford核心语句
				dis[v[k]] = dis[u[k]] + w[k];
			if (book[v[k]] == 0)	//book用于标记v[k]这个点是否在队列中
			{
				que[tail++] = v[k];
				book[v[k]] = 1;
			}
			k = next[k];
		}
		//出队
		book[que[head]] = 0;
		head++;
	}
	//下面就是输出1号到各个顶点的最短路径
	for (i = 1; i <= n; i++)
	{
		printf("%d ", dis[i]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值