虽然Bellman-Ford 算法的思路很简洁,但是O(VE)的复杂度很高,然后怎么办呢?优化嘛,Bellman-Ford 算法的每轮操作都需要操作所有边,显然这个其中会有大量无意义的操作,严重影响算法性能,
只有当某个顶点u的 d[u]改变时,从它出发的边的邻接点v 的d[v]才有可能改变
是不是想到了什么???对了就是队列。
我们建立一个队列,每次将队首的顶点元素u取出,然后对u出发的所有边都松弛一遍,就是判断 d[u] + length[ u->v ] < d[v] 是否成立,如果成立就用 d[u] + length[ u->v ] 覆盖 d[v] 于是d[v]获得更优值。如果v不在队列中就加入队列,这样操作指导队列为空(说明图中没有从源点可达的负环),或是某个顶点的入队次数超过V - 1(说明图中存在从源点可达的负环)
伪代码
queue<int> Q;
源点入队
while(队列非空){
取出队首元素u;
for(u的所有邻接边u->v){
if(d[u] + dis < d[v]){
d[v] = dis + d[u];
if(v 当前不在队列里){
v入队;
if(v入队次数大于n-1){
说明有可达的负环, return ;
}
}
}
}
}
优化后的算法叫SPFA,期望时间复杂度为O(kE)E为边数,k是常数,如果有负环,退化为O(VE).
vector<Node> Adj[MAXV];
int n, d[MAXV], num[MAXV];
bool inq[MAXV];
bool SPFA(int s) {
memset(inq, false, sizeof(inq));
memset(num, 0, sizeof(num));
fill (d, d + MAXV, INF);
queue<int> Q;
Q.push(s);
inq[s] = true;
num[s] ++;
d[s] = 0;
while(!Q.empty()){
int u = Q.front();
Q.pop();
inq[u] = false;
for(int j = 0; j < Adj[u].size(); j++){
int v = Adj[u][j].v;
int dis = Adj[u][j].dis;
if(d[u] + dis < d[v]){
d[v] = d[u] + dis;
if(!inq[v]){
Q.push(v);
inq[v] = true;
num[v] ++;
if(num[v] >= n) return false;
}
}
}
}
return true;
}