全部博文(372)
2012年(372)
分类: 虚拟化
2012-03-05 14:23:46
Bellman-Ford可以用来解决含有负权图的单源最短路径(Dijkstra不能解决这种问题)。代码简单,但是效率低。具体的过程就是不停的松弛,每次松弛进行一次更新。如果更新n次仍然还可以更新,说明图中存在负环,直接跳出就可以。这种情况下无解。
Bellman-Ford的时间复杂度是O(ve).
伪代码如下:
SPFA算法
因为Bellman-Ford的算法冗余的部分太多,有很多不必要的计算。所以可以用队列对其进行优化,这就是我们所说的SPFA算法。由西南交大的在1994年提出。SPFA同样是解决含有负权图的单元最短路径。其实就是对Bellman-Ford的优化。据说还有一些八卦的消息,说SPFA其实就是Bellman-Ford算法,而现在流传的Bellman-Ford是山寨版,这里就不深究了,哈。
SPFA算法的思路就是:设立一个队列,优化队列中的元素u的dis[u],并对每一个与u相连的节点v进行松弛。如果dis[v] > dis[u] + w(u, v),dis[v] = dis[u] + w(u, v),并且如果当前的v不在队列里边(inqueue[v] == false),则v入队列并标记inqueue[v] = true。不断松弛,直到队列为空。
这样看来SPFA很想BFS,其实写法上SPFA类似BFS,不过有一点不同的是。对队列中的节点u取出并且松弛完一次与u相邻的所有节点v以后,将u再次标记为不在队列,即inqueue[u] = false;这样才能实现对u(也就是每一个节点)进行多次松弛,直到队列为空。
当然,如果图中含有负环,松弛会一直进行下去(总能找到div[v] > div[u] + w(u, v))。从Bellman-Ford中知道,一个节点最多被松弛n次,如果超过,则说明有负环,无解。这里可以加一个cnt[]记录每一个节点出现过的次数。保证cnt[i]<=n,否则跳出。
SPFA算法期望的时间复杂度是O(ke),其中k是每个节点尽对的平均次数。k一般是小于2的。
实现代码如下:
SPFA算法有两个优化算法 SLF 和 LLL:
SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)
SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。
总体比较:
在Bellman-Ford算法中,要是某个点的最短路径估计值更新了,那么我们必须对所有边指向的终点再做一次松弛操作;
在SPFA算法中,某个点的最短路径估计值更新,只有以该点为起点的边指向的终点需要再做一次松弛操作。
spfa算法能够避免很多冗余的松弛,使O(ve)的复杂度降到O(ke).