算法过程
初始时将源点加入队列,每次从队首取出一个定点,并与其出边进行松弛操作,如果松弛成功则将该出边的终点加入到队列中去,处理完毕该点的所有出边后,出队列,继续进行上述操作,选取队列的顶点,直到队列为空,算法结束,这里需要用一个book数组进行记录目前队列中存在哪些点,如果对某一条边松弛成功,但是该出边的终点在队列中,那么久没必要把该点插入到队列中,因为队列中存在该点,会对该点的说=所有出边进行松弛操作,通过队列优化的Bellman-ford算法如何判断一个图是否存在负环(这里一开始对负环的理解出现了错误,导致一直不理解如何判断存在负环,负环指的是整个环的的权值之和为负值,而不是存在着权值为负值的边的环,- -…)?如果某个点进入队列的次数超过n次,那么这个图则肯定存在负环
用队列优化的Bellman-ford=算法的关键之处在于:只有那些在前一遍松弛中改变了最短路径估计值的点,才可能在引起他们邻接点最短路径的估计值发生变化
该算法的时间复杂度在最坏的情况下也是O(NM)
#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define inf 0x3f3f3f3f
#define maxn 20
using namespace std; //求的是顶点1到其余各顶点的最短路径
int head[maxn];
int n,m;
struct node{
int front,to,w,next;
};
node edge[maxn];
int book[maxn];//标记哪些点已经在队列中
int dis[maxn];
int temp[maxn];//用来存放同一个顶点进入队列的次数
queue<int> q;
int main()
{
int flag=0;
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
//有向图 ,邻接表的建立
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&edge[i].front,&edge[i].to,&edge[i].w);
edge[i].next=head[edge[i].front];
head[edge[i].front]=i;
}
//dis数组的初始化
dis[1]=0;
for(int i=2;i<=n;i++)
dis[i]=inf;
q.push(1);//源点进去队列
book[1]=1;//源点被标记
temp[1]++;//源点进入队列的次数加一
while(!q.empty())//判断队列是否为空
{
int k=head[q.front()];//选择队列的顶点
while(k!=-1)//对队列顶点的出边进行松弛操作
{
if(dis[edge[k].to]>dis[edge[k].front]+edge[k].w)
{
dis[edge[k].to]=dis[edge[k].front]+edge[k].w;
if(book[edge[k].to]==0)//如果没在队列中们就插入到队尾
{
q.push(edge[k].to);
book[edge[k].to]=1;//进入对了,就标记为1
temp[edge[k].to]++;//同时改点的进入队列的次数加一
if(temp[edge[k].to]>n)//判断是否存在进入队列超过n次的点,存在的话就存在负环,不会出现最短路径
{
flag=1;//用来跳出两个while循环
break;
}
}
}
k=edge[k].next;
}
if(flag)
break;
book[q.front()]=0;//出队列后取消标记,因为同一点可能会多次进入队列
q.pop();//顶点出队列
}
if(flag)//flag为1,则存在负权回路
printf("存在负权回路\n");
else
{
//不存在负权回路,打印顶点1到其他顶点的最短路径
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
printf("\n");
}
return 0;
}
/*
输入数据
Input 1:
5 7
1 2 2
1 5 10
2 3 3
2 5 7
3 4 4
4 5 5
5 3 6
Input 2:
3 3
1 2 1
2 3 2
3 1 -3
Input 3:
3 3
1 2 1
2 3 2
3 1 -4
输出结果
Output 1:
0 2 5 9 9
Output 2:
0 1 3
Output 3:
存在负权回路
*/